Queued (Planned)
FW.CsharpHtmlToXslt
Replace hand-built C#-HTML string builders with ProjectModel(XML, XSLT) (the DNA way). Ranked by mass: ListsController (~800 lines across 6 form/table/CSV methods - biggest) ; PagesController page-directory (~40) ; ShipBoardRenderer.ElementConsole control fragments (~30) ; AboutController markdown assembly (~120, text not HTML - XML model + markdown-emitting XSLT, or a builder). Each becomes an XML model + an embedded .xslt. ListsController alone is its own cycle.
FW.PrettySubsiteStreamPassthrough
PrettySubsitePathMiddleware swaps Response.Body for a MemoryStream on every request to rewrite /sites/X/sites/Y links, so it FULLY BUFFERS every response body before flushing. V25.41 added a narrow streaming bypass (IsStreamingRequest: /agui/* path or Accept: text/event-stream) to unblock the /live SSE hang (anti-pattern #28), but the broader defect remains: every NON-HTML body (a large DB-backed attachment/document download via ListsController.AttachmentDownload, DocumentController.Download, StudioExportController) is read entirely into managed memory before a byte reaches the socket - a latent OOM proportional to file size x concurrency, and a client-visible hang on a large/slow stream. Principled fix: replace the unconditional MemoryStream swap with a content-type-aware PASS-THROUGH stream that, on first write, inspects Response.ContentType and buffers ONLY text/html (to rewrite), streaming everything else straight to the original body. Subsumes both the SSE case and the download case and retires the bypass special-case. Verify with a large-download regression test asserting bytes are NOT fully buffered, plus the existing /agui live smoke.
V22.0Legacy ContentType.LeaveRequest migration (candidate)
Carry-over from V18.5b; the one real feature debt left after V21. Theme not yet confirmed by user.
DECISION (user-confirmed 2026-05-26): CONSOLIDATE onto ContentType.HrLeaveRequest - retire legacy ContentType.LeaveRequest + Workflow.RouteLeaveRequest + ER.OnLeaveRequestSubmitted, repoint central HR blueprint, update HrAgencyTests/OpenApiTests/V190. Staged cycle. Deferred behind the agent-onboarding triage.
LogicalMachineCatalog
OPERATOR (2026-05-30, "later"): enumerate the LOGICAL machines we know we will need - the complete catalog of Tool/IToolInvoker machines the platform requires, so the GAPS are visible (what exists at /machines today = 35; what SHOULD exist per the domains msg/task/crm/erp/manufacturing + the Typed Envelope arc: a SealPackage machine, a GuardrailValidate machine (Schematron/SVRL, E3), a Render/Represent machine per format (md/xml/pdf, E4), per-domain CRUD/transform machines, etc.). Deliverable: a gap-analysis catalog (have vs need) - reuse the FMECA/criticality lens (KB.ShipCriticality) to prioritise. Read-only census first (parts-xq over //Machine + the Tool catalog), then the needed-machines list. Pairs with the Typed Envelope arc (plans/TypedEnvelope.md) and Agency Contract Piece B.
Observatory.QuestBoarda 'things to do' quest board - a game-like panel of available Observatory actions (call a meeting, run a pipeline, ask an agency, approve work) so there is always something to click and do
Slice 3 of "make the Observatory playable" (operator: "let me do more things to do and see like in games"). Reuse-first: a projection of the available Tools/pipelines/pending-approvals as a clickable action list (each row fetches /agency/tools/{Tool}/invoke - the V25.53 ping pattern), with feedback rendered from the result + the bus animation. Maps onto the Board-lens pattern (a Board.X over the tool registry + approvals + pipelines) + the inspector-action seam. No new engine.
A2ui.FormsInteropadopt A2UI/AG-UI in the forms - PARKED behind Interop.A2aAgentCards (interop-gated): re-express SP-on-prem ContentType forms as an A2UI surface projection so external agent-UI hosts can render+drive them
Ideation 2026-06-10 (docs/ideation/2026-06-10-a2ui-forms-ideation.md), operator decision: PARK behind Interop.A2aAgentCards. Verdict: internally A2UI adds little - SPICE already has the better-fit, more-DNA-aligned seams (declarative ContentType XSLT forms, Tool.ListSchema schema projection, ProvisioningDelta write path, the approval gate); an agent wants schema+data, not a rendered UI tree. The genuine added value is EXTERNAL interop / marketplace distribution - A2UI is the UI half of what A2A cards are for agents. For the human forms the cheap slice IS 'just a custom view the SP-on-prem way' (a third XSLT projection off the ContentType, like an XsltListViewWebPart view) - it stops being just a view only where the renderer is a foreign agent client (no .NET A2UI renderer exists - the real cost, idea #5) and the round-trip is a live typed interruptible agent conversation. UN-PARK trigger: AFTER Interop.A2aAgentCards ships AND external interop demand is real; un-park seam = idea #4 (advertise an A2UI capability in the agent card, ride the existing MCP/A2A transport). Survivors 1-6 detailed in the ideation doc; ideas 1-2 are one-XSLT-adaptor cycles, #5 is the renderer fork, #6 (Genesis draws a form -> proposes a ContentType) is the self-build payoff. Gated by the strategy verdict (no-go on heavy new surface until fabrics hardened).
Approvals.OneClickExtranetone-click / interrupt-style approve on the approval gate - PARKED until high maturity, EXTRANET-ONLY activation (external reviewers, not the internal city); needs ZERO A2UI
Ideation 2026-06-10 (the genuinely-now-able UX slice split out of A2UI idea #3). A one-click (or AG-UI-interrupt-style) approve affordance on the shipped ApprovalRequest gate (ContentType.ApprovalRequest RequestStatus Open->Approved -> ApprovedDeltaApplier seals the carried ProposedDelta), so a reviewer approves in one action instead of editing the row. Needs NO A2UI - a plain SPICE cycle (a board/form action over the existing gate). Operator decision: DEFER until the platform reaches a HIGH MATURITY level, and when built ACTIVATE ONLY ON THE EXTRANET (external-facing reviewer zone) - internal operators edit the row directly; the polished one-click surface is an external-partner concern (SP-on-prem intranet/extranet split). UN-PARK trigger: high platform maturity + an extranet/external-reviewer surface exists. Reuse-first: the gate, ApprovedDeltaApplier, and the lists action seam already exist; this is UX + an extranet scope guard, not new engine.
SelfBuild.AutoGenesisreactivate the self-build loop - Reception senses the top-N needed agencies and builds them (the paused Voyager city-genesis loop)
Ideation survivor #6. The core self-build loop is PAUSED: divisions are manually initiated; the demand-driven agency-genesis (the city finds + queues the top-N needed agencies and builds them) is designed but not activated. This is the North Star's beating heart (Claude builds the city + coaches; the city's divisions do the missions). Most ambitious; best AFTER SkillLibrary.ExtractSkill (auto-built agencies get reusable skills) and EvalLoop.Observatory (tells it whether its self-builds are winning). -> /ce-brainstorm before building.
SpField.VocabularyAdoptionadopt the SharePoint Field-element vocabulary into FieldDefinition (Layer 1 = ~45 semantic attrs plus rename 4 drifts to SP-canonical plus cheap XSLT/Schematron consumers); Layer 2 = EF JSON-column queryable store
Plan: docs/plans/2026-06-07-002-feat-sp-field-vocabulary-adoption-plan.md. Token-deferred. Layer 1 cheap (XML/XSD/XSLT plus 1 record/handler); the 514-instance rename is the expensive part (fidelity only). Layer 2 (queryable store) is its own arc. Polish/richness tier - after stabilize.
BRAINSTORM.25Owed from BRAINSTORM.23: analyse the adopted features with domain specialists (reuse, update or generate behind approval), diagrams as content, the rest of InfoPath's rules, AddFormTemplate for the builders, actor rows that follow their parts, SharePoint's real web-template vocabulary
(1) Workflow.AnalyseAdopted on a node after the benchmark: how/why/when for the approved features; a specialist step that finds a declared ActorProfile by domain (node Topics), else proposes one (AddActorProfile in an approved delta) and asks it; outputs a feature table, a quadrant chart (impact x effort) and a roadmap as ContentType.Diagram items (DiagramType + MermaidSource in a Brainstorm Diagrams library, rendered with the vendored mermaid.js) and a deck via Phase.GenerateSlides into Brainstorm/SlideDecks. (2) InfoPath's remaining rules, each with its engine: Calculations/Calculation (Target, Expression, Refresh), RuleSets/Rule with AssignmentAction and SwitchView, ConditionalFormatting (hide/readonly when), QueryConnection to a SavedQuery. (3) AddFormTemplate builder op (the AddPartOp shape) so parts builders can make forms. (4) ActorProfileSeeder never updates an existing Hub/Actors row, so a part edit (Goal, SkillCatalog) does not reach the live actor - decide the SharePoint answer (feature upgrade / reprovision) and fix. (5) Store SharePoint's real web templates (WEBTEMP: STS#0, BLOG#0, BDR#0, SRCHCEN#0 ...) and the Fab 40 application templates as a standard vocabulary so the copilot adds real ones instead of plausible inventions. (6) A scoped MCP profile per agent kind (e.g. the map tools only) for agents outside SPICE - the full /mcp/jsonrpc catalogue is too much for a small model; inside SPICE the SkillCatalog already scopes tools. (6b) Triage of agent-filed issues: Tool.FileIssue rows on /sites/Projects/Lists/Issues need an owner and a weekly look. (7) A review surface for self-written actor memory (RememberThis notes can be wrong and bias every later answer). (8) A map-wide review mode for proposals (walk them, approve or reject by keyboard). (9) Turn waste: the copilot reads the same node with detail=full several times per question. DONE in BRAINSTORM.29: (3), (6b) owner, (9); (4) and (5) in BRAINSTORM.27. Left: (1), (2), (6) named profiles, (6b) weekly triage, (7), (8), and real web parts in the chat or on a node.
SPWAY.30Split the spine the SharePoint way: a hive Feature folder (TEMPLATE/FEATURES/Name/feature.xml + elements.xml) carries ANY SAF part, so Parts.xml (930 KB, 7.6k lines) is cut into feature folders per capability; C# counterparts scaffolded by XSLT 3.0 text output over the spine
Operator 2026-10-01: "can't we split the xml smartly and use refs to include them, the file is getting too big - more parts like plugins, and C# counterparts scaffolded" and "what about T4 templates (.tt)?". Today: HiveFeatureContributor already loads TEMPLATE/FEATURES folders as parts (IssueTracker), but elements.xml only knows Field, ContentType and the feature verbs. The 15 apps already ship their own Parts.*.xml. ContentTypeCodeGenerator (SPMetal analogue) generates ContentTypes.g.cs, and /spice-new-part scaffolds a kind. Plan: (1) elements.xml accepts any SAF part element in the Parts namespace - an engine-seam change, so ask first; (2) move one capability (MapCrew + ShapeOutcome) into a feature folder as the proof, served unchanged; (3) codegen as an XSLT 3.0 method=text sheet on Saxon (a declared mapper, debuggable in XMLSpy), not T4: a .tt keeps C# logic inside a template outside the parts, and needs a second template engine (dotnet-t4 / Mono.TextTemplating). XInclude was rejected: the loader would have to resolve includes, and SharePoint's own unit of modularity is the Feature.
Shipped - most recent first (last 40)
INFRA.2Timer job control: Run now, Enable, Disable and the last result, from Central Administration
Differs from SharePoint: the job's on/off lives on its SchedulerState row and wins over the declared default (Scope.Scheduler); only the last run is kept on the board (no per-job history list yet - workflow runs are in Workflow History); the actions are for the operator only (this machine, or a person signed in). The scheduler is one singleton in two roles (timer and Run now), so Run now works where the timer is off. Also this session: the global navigation organised for a person (hubs, Plan, Make a request, Approvals, Digest, Sites, Boards, Central Administration); AddSiteBlueprint refuses a name that is not a URL name; enterprise groups (corp\Name nested one level); the agent card is the service contract; GOV.13 sign-in; the system-steward and pmo seats.
TPL.34ListInstance Data: a Feature's default rows; Feature.ProjectSiteDefaults stapled to the Project Site
Differs: rows land only into an EMPTY list (stapling re-activates at every boot, so a deleted row never returns); rows are keyed default-{title}; ignored on a SiteTemplate's DeclareList. Fixed on the way: activation redeclared lists the site template already declares; the row reader needed a scope. Known gap: the stapling pass does not reach the Demo__Projects subsite (Risks and Links stay empty there).
TPL.33Project Site template with Project Server's project-site lists
Differs: PROJECTSITE#0 merged with the Project Server project site (Risks, Deliverables, Issues) in one template; ContentType.Risk carries Project Server's columns; a Tasks view Url may not take a reserved projection name (TaskCalendar.aspx). Views are ghosted: ADR adr-views-ghosted-pages on Engineering.
TPL.32Gantt view (?view=gantt)
Differs from SharePoint: drawn by Mermaid gantt (the source is shown to copy); XSLT 3.0 on Saxon (version=3.0 routes there) for grouping and date arithmetic; bars from StartDate, else the creation day; undated tasks listed as not scheduled.
STAFF.2A dispatched request lands as a Workflow Task for its agency, and completing it resolves the request
Reuse of TPL.17, no new part kind: Workflow.ReceiveAndDispatch gains Step Kind=Task Assignees={IncomingRequestTarget} and SetField IncomingRequestStatus=Resolved, FallbackSeat=claude-code; Reception gets a Tasks list (SharePoint fixes the task list at association, so the task lands on the desk's list, assigned to the agency - not on the agency's site as the row first said). Engine fixes in the shared seam: Park succeeds only when a task row actually landed; an unassignable task step is not stamped Completed (a NeedsAgency request looked done); a resume past the phases is not "ran no phase" (it would have filed a fallback task on every completed approval resume). Full-circle test live: req-news-to-tiktok (news to TikTok conveyor, scouted acceptance criteria: facts only, human gate, AIGC label, Inbox drafts until the API audit) was dispatched by the local model to Agency.Content and its task landed. Honest limit: nothing on the agency side acts on the task yet (INTAKE.6); operator direction: the task carries its payload in an InfoPath form and each party edits only its part (STAFF.6, server-side ReadOnly is the missing piece). Staff audit filed on GOV.12b-e: persona and role on all 47 actors, 0 actor RACI rows, 5 empty SkillCatalogs, 21 actors without a workflow, fallback on 7 workflows.
STAFF.1Progressive disclosure that actually discloses: a briefing's knowledge slot resolves to the article (Query.KnowledgeById), the Reception desk slot reads its row limit, and the dead JIT context code is gone
First cycle of the staffing + JIT context module (operator 2026-10-02: "start with default staff and hire when needed like in paperclip ... we already had an algorithm for JIT context, see if it can be all in one module"). The architect's map: the real JIT algorithm is Tool.ComposeBriefing (Scope.SeatBriefing layers with char budgets, dedupe, quota scaling, topic on demand); BriefingBuilder repeats part of it in C#. Found broken: the default knowledge bundle was offered as slots whose Source KnowledgeById('...') had NO resolver - Tool.FetchKnowledge answered "No resolver", so an agent only ever had the one-line description, and the only test checked the prefix, never fetched. Now: Query.KnowledgeById (a Parts SavedQuery cloned from Query.KnowledgeByTag, Id filter, role and classification gated, ViewFields Id/Description/Body) and the slot points at it; the test FETCHES a slot as the research phase's Lead and finds the article body (an actor-less call is right to see nothing above Assistant). Phase.DispatchRequest's slot said RowLimit=10, the resolver reads limit= - fixed. Deleted: JITContextInjector.cs (no reference), the duplicate AddSingleton<ContextManager> in SpiceInfrastructureExtensions, BriefingBuilder's User item (read a UserContext nothing set; a singleton would leak one caller into another's briefing). Deferred: trimming IContextManager's key-value members (several consumers and fakes implement the interface). A trap on the way: a part copied from the namespace-stripped spice-xq view loses its caml: prefixes - copy from spice-edit show.
TPL.31All Sites search about three times faster: the search reads each readable list's stored rows instead of building its whole page model, with the item page's own content-approval trim - a pending item stays out of a reader's results
Measured first: SPICE has no local search index (the SearchProvider parts are web search services), and an All Sites query took 2.4-3.1 s warm on :5198. The cost was BuildListView per list - the list page's model (sorting, lookups, calendar groups, navigation) built only to read cell values. The loop now reads the stored rows (IListItemEnumerator) and keeps the same trimming in two layers: the list must be readable (EffectivePermission, TPL.23) and not hidden, and each row must be visible to the caller (ContentApproval.IsVisible - a pending or rejected item only to its author and the approvers), the rule the item page uses. A match is any non-engine column (not _-prefixed). Warm queries now 0.9 s (first after boot 1.8 s). The ponytail note stays: a crawled index (SharePoint's search service) is the upgrade when a scan no longer fits.
Served: a pending document in Demo__Docs is absent from a reader's All Sites results and present for the System Account (an approver); the earlier trimming, refiners, XML and default-scope assertions still hold.
TPL.30InfoPath's data connection to a SharePoint list on the Form Library: a form's Lookup column fills its dropdown from its list (and a Taxonomy column from its term set), as the item form always did - the expense claim's Approver comes from the Directory's People
Reuse, not a new mechanism: InfoPath's "receive data from a SharePoint list" connection feeding a dropdown is what a SPICE Lookup column already is (LookupSite, LookupList, LookupDisplayField). The item form (BuildItemForm) loaded the options; the template form a Form Library's New uses (BuildTemplateForm, TPL.24) did not, so a lookup on a form rendered an empty select. It now loads Lookup options (LoadLookupOptions / FormLookupOptions) and Taxonomy terms the same way. Field.ClaimApprover (Lookup into Directory/People) on ContentType.ExpenseClaim. An agent writes the approver as the person's key (claude-code) and it lands in the column and in the form's XML. Not built: InfoPath's other connections (submit to a web service, query a REST source) - the list connection is the one a form library uses.
Served: the Demo claim form's Approver select lists claude-code (seat) from Directory/People; a REST claim with ClaimApprover=claude-code files it in the column and in Body.
TPL.29InfoPath's views on a Form Library: a FormTemplate declares named views, each a stylesheet over the filled form's XML document; the item page links them and .../Item/{id}/FormView?name= renders one - a printable expense claim sheet as the worked example
InfoPath's manifest.xsf xsf:views - one XML document, several XSL views - mapped onto what SPICE already keeps: every filled form's myFields document is its Body (TPL.24), so a view is a stylesheet over it. FormTemplate gains View (Name, XslLink: a path under wwwroot/XSLT, the same guard as a list view's XslLink, TPL.20); ListsController.FormViewOfItem reads the item through BuildItemForm - the item page's own permission and content-approval trim - parses its Body and renders it (the root carries Site, List, ItemId, View); the display form lists the views (FormViews). Worked example: Form.ExpenseClaim's Print view (XSLT/FormViews/expense-claim-print.xslt): the claim, its lines, the promoted total formatted 0.00, signature lines, @media print. Two refusals are 404s: a view the form does not declare, and a list that is not a form library.
Served on the Demo site collection: the seeded Berlin claim's page links Print; the view shows Train tickets 189.40 and the total 499.40. A test assumption corrected on the way: a subsite links in its nested form (/sites/Demo/sites/Docs/...), the convention the Edit link already uses - both forms serve. Next: data connections (a dropdown from another list).
TPL.28A Demo site collection for showing and testing: /sites/Demo (Team Site) with a subsite per enterprise template (Docs, Records, Blog, Projects, Absence, Review), its workflows associated, sample content seeded through the real doors, and the claude-code and spice-verify seats as Members
Operator 2026-10-02: "i want a demo sitecollection also for your testing. how can we have that in here?". The SharePoint way, no new mechanism: a site collection is a top-level SiteBlueprint with Subsites (DemoPortal -> /sites/Demo, and /sites/Demo/sites/Docs etc.), each subsite applying one of this arc's templates; the Demo home adds a survey and Promoted Links tiles; Approval and Collect Feedback are associated on Demo__Docs' documents, Three-state (with its association Data) on Demo__Projects' issues. Sample content is SharePoint's demo-content feature as a seed file (SeedDeltas/Demo.xml - the folder is the registration, idempotent per key, re-applied when the file changes); every row goes through the real doors, so the seed is a check in itself: the Berlin claim's lines pass the claim form and promote ClaimAmount 499.4, the issue starts Three-state and files a task for claude-code. The Members group holds claude-code and spice-verify (Contribute) - the seats may write here, which the working sites refuse (DocumentCenter is Read for the seat), so smoke runs and agent demos stop leaving rows on real sites.
Served: every Demo site serves its lists with SharePoint's template numbers (170, 102, 106, 115, 101, 301, 1100); the seeded tiles, survey, promoted claim and three-state task are there. On :5198 the seat completed the demo issue's first task over MCP and the issue moved to Resolved. Not built: a reset (delete the rows; a changed seed file re-applies) and moving the earlier tests' probes from ProductTeam/DocumentCenter to Demo.
Follow-up the same day (operator: "ok do whats needed"): the working sites were cleaned of this arc's demos - 19 rows deleted (ProductTeam survey answers, DocumentCenter policies with their approval tasks, two claims, ProjectTracker issues with their tasks); the demo-only lists left ProductTeam (TeamSurvey, PromotedLinks) and DocumentCenter (ExpenseClaims); EventReceiver.ThreeState.ProjectTracker removed - an ItemAdded workflow on a working site filed a task for every real issue; it lives on Demo__Projects. The writing served tests (survey, tiles, one response per person, Approval/Three-state, Form Library, search probes) now run on Demo sites; the read-only template checks stay on ProductTeam and DocumentCenter.
TPL.27InfoPath's repeating table in the browser: the Form Library's New form draws a section's lines as a table (add and remove rows, a running promoted total), posts them as the same JSON an agent sends, keeps them on a refused submit - and every Number field in SPICE's forms now takes decimals
TPL.26's browser half. BuildTemplateForm carries each RepeatingSection (its columns from the line content type, the current lines, a promotion that reads one of its columns) and does not let the browser demand a promoted column (the server fills it); FormView.xslt draws the table (row inputs without a name, a hidden input named after the section column, + Add line, Remove) and loads js/repeating-section.js only on a form that has a section. The script writes the non-empty rows into the hidden input as a JSON array of objects - the value an agent sends, so one server check covers both - shows a sum/count promotion in its field as you type, and re-runs the form's live validation 400 ms after typing stops (the existing listener only hears change, which fires on blur, so a line message stayed after the line was completed - seen in the first browser run).
Found on the served surface, in a real browser: (1) every Number field in FormView was type=number without a step - the browser refused any decimal (499.40) silently and the form never posted, on every SPICE form, not only this one; now step="any". (2) The cross-line rule used xs:decimal(LineAmount), which raises on a half-filled line ("Cannot convert '' to xs:decimal") - the data now says number(LineAmount), false for an empty amount. Both written with the Write tool / chr(92) rather than a heredoc.
Served: the New form draws the section (hidden ClaimLines, the line columns, + Add line, the script, the promotion, an amount input with step="any" and not required); a refused claim (a 1300 line) comes back with its lines in the rows. In Chrome via Playwright on :5198: two typed lines (189.40, 310) showed a running total of 499.4, submitted, and the stored claim reads ClaimAmount 499.4 with both lines nested in its XML (Berlin client visit, left as a demo).
TPL.26InfoPath's repeating section and property promotion in the Form Library: a claim's lines travel as a JSON array (what an agent writes), are checked line by line against their content type and as a whole by the form's XPath rules, nest in the form's XML, and their total is promoted to a column - at every door, and described in the agent schema
Design call (advisor): the lines live INSIDE the form's XML, not as child rows with a Lookup - the point of a repeating section is the cross-line rule (sum of lines, one line over a limit), and only a payload that holds every line lets one XPath see them at write time; lines-as-child-rows stays the ERP's pattern (QuoteLines, InvoiceLines). Reuse-first storage, no new field type: the section is a hidden Note column (ClaimLines) holding a JSON array of objects - what an agent sends over REST or MCP (field-ClaimLines=[...]) and what the browser table will post. FormTemplate gains RepeatingSection (Field, ContentType = the line schema, Element) and PromotedField - InfoPath's own .xsf listProperties vocabulary (columnName/node/aggregation: sum, count, first, last, merge), not a free expression; a promotion with no node leaves the column as given, so a claim without lines keeps its typed amount. FormLibrary.Payload nests the lines (myFields/ClaimLines/Line/LineWhat...), Errors validates each line against ContentType.ExpenseLine (refusals named ClaimLines[1].LineAmount, "Line 2: ...") and a non-array as "must be a JSON array of objects", Stamp promotes through the XQuery engine before the rules run - so the promoted total meets the existing amount rule. The list model and ListSchemaJson.xslt add repeatingSections (field, format, element, lineFields) and promoted to x-spice-form.
Parts: Field.ClaimLines (hidden on ContentType.ExpenseClaim), ContentType.ExpenseLine (LineWhat, LineAmount; no parent - a line of the XML, never a row), Form.ExpenseClaim gets the section, PromotedField ClaimAmount = sum(ClaimLines/Line/LineAmount) and a cross-line rule: a single line over 1000 EUR goes through a purchase order.
Served: schema names the section and its line fields; REST lines 40 + 380 file with ClaimAmount 420 and the lines nested in Body; six lines of 900 refused by the promoted total; a 1200 line refused by the cross-line rule; a line without an amount refused as ClaimLines[1].LineAmount; prose instead of JSON refused; an MCP publish with a 1500 line refused; an edit adding a 55 line re-promotes 475. Next (TPL.27): the browser's repeating table.
HARNESS.18spice-edit insert/replace read a fragment in the file root's namespace context: a part with caml: views (MySite, Brainstorm) goes back through show/replace instead of failing, keeps its caml: prefixes, and an unchanged part writes nothing
Filed in TPL.16, when spice-edit replace refused SiteTemplate.MySite and SiteTemplate.Brainstorm ("'caml' is an undeclared prefix") and two DeclareLists had to be stamped by an in-span text patch. Fragment() wrapped the input in an element declaring only the default namespace; it now carries every prefix the file root declares, and puts those declarations back on each fragment element so XLinq serialises caml:Where as caml:Where (the first fix alone parsed, but rewrote every view as <Where xmlns="...Caml"> - semantically equal, line churn); Serialize already drops the declarations the root makes. Checked by hand: show + replace of SiteTemplate.MySite unchanged -> "no part changed - nothing written"; one Description changed -> one DeclareList changed. Ceiling: a replaced part is serialised whole, so a sibling whose attributes were spread over lines comes back on one line (MySite: 3 insertions, 4 deletions for a one-attribute edit) - an attribute-level patch is the upgrade (HARNESS.17 owed: set Id/xpath).
TPL.25Search results the SharePoint way: each result shows its title, a refinement panel counts the results by site and by content type and narrows them (?site=, ?ct=), and ?format=xml serves the same results to an agent
TPL.23's open ends. A result read "List > item 8FB867E3"; it now links its Title (the item id when it has none) with its list beside it. SharePoint's refinement panel: counts by Site and by Content type over every result, each value a link that narrows the search, All lifts it; a refiner with a single value and no refinement is not shown. For agents (the operator's standing ask): ?format=xml returns SearchResults (Query, Scope, Count) with its Refiner/Value counts and one Result per hit (Title, Site, List, ItemId, ContentType, Url, Field, Modified, snippet) - the pattern Site Contents' XML set. Security trimming is unchanged (TPL.23): refiners count only what the caller may read.
Served: the All Sites XML carries the probe's title and site, the Site refiner counts the private site for the System Account, ?site=ProductTeam leaves only ProductTeam results, and the page shows the title. The test takes 31 s - several All Sites searches; the index (ponytail note in SiteSearchController) is the upgrade.
TPL.24SharePoint's Form Library (115) the InfoPath way, as handy for an agent as for a person: a list names its FormTemplate as DocumentTemplate, every write - browser form, REST, the MCP/AddListItem op, an edit - is checked by the form's XPath rules and filed with its answers as columns and the filled form's XML in Body; the list schema an agent reads names the rules
Operator mid-cycle 2026-10-02: "keep in mind has to be handy for llm agents too". So the rule is one Foundation helper (SPICE.Foundation.Forms.FormLibrary: For / Stamp / Errors / Refusal) called at every door: ListsController New (the form template rendered by FormView, live-validated by a ValidateForm route, a refused submit returned with its answers and the rule's message), ListsApiController create (400 with every field's message, so a caller corrects and resends), AddListItemOpHandler (the op tool_publish_to_list and every delta go through; reads the list's DocumentTemplate from the manifest like IsModerated - new SiteTemplateExpander.ListAttribute) and ItemUpdateService (the merged row must still pass; its XML is rewritten). The initiation form's check is the same helper now. For discovery, the list model carries FormTemplate/Rule and ListSchemaJson.xslt emits them as x-spice-form {id, name, rules[{field, errorWhen, message}]} - tool_list_schema tells an agent the rules before it writes; no tool code.
Parts: ContentType.FormDocument (SharePoint's Form, 0x010101: Body hidden - the myFields XML), ListTemplate.FormLibrary 115, and a worked example: Form.ExpenseClaim on ContentType.ExpenseClaim (ClaimAmount, ClaimCategory, ClaimDate, ClaimPurpose) with two rules - an amount between 0.01 and 5000 EUR, and a purpose for equipment (a sibling rule: ../ClaimCategory) - as the Document Center's ExpenseClaims library. Field.Amount was not reused: it means a bid total.
Not built (brainstorm-13 stays open for them): several named views per form (InfoPath views), data connections, repeating sections, and a DeclareList DocumentTemplate for a site template.
Served: the agent schema names the form and its rules; REST refuses 9000 EUR (ClaimAmount) and an equipment claim without a purpose, files a good one (ContentType.ExpenseClaim, the XML in Body); an edit to 0 is refused; an MCP publish of 7000 EUR is refused with the form's message; the browser form returns a refused claim with the message (screenshot of the form). On :5198 a 6200 EUR browser claim was refused and a client lunch filed over REST.
TPL.23SharePoint's Enterprise Search Center (SRCHCEN#0) and its All Sites scope, security-trimmed - and the site search page, which had no permission gate at all, is now a gated site door
Found while adding the scope: /sites/{site}/_layouts/search (SiteSearchController) ran outside ListPermissionGate - any caller could search any site, a private My Site included, and scope=hub read every joined site's lists (hidden ones too) with no check. Now the controller runs through ListPermissionGate.RunFor like the site home (403 without read on the site), and every hit is trimmed: a list is searched only when EffectivePermission gives the acting principal read on it (the System Account and a host without the seam as before), never a hidden list (actor memory). New: scope=all - SharePoint's "All Sites" - over ISiteNavigator.AllSites() (the manifest walk), and the scope switch (This site / Hub / All Sites) on every search page. SiteTemplate.SearchCenter (SRCHCEN#0, Pages) with a Search site in the manifest; a site of the SRCHCEN definition searches All Sites unless the query names a scope (ISiteNavigator.WebTemplateFor - an empty scope= is "This site": ASP.NET binds it to null, so the check is the query key, caught by the test). WEBTEMP SRCHCEN#0 points at the built template.
Ceiling (ponytail comment): All Sites builds every readable list view per query - 13-19 s in the served test on this farm; the upgrade is crawling into the SearchProvider index. Not done: result titles (hits show list and item id, as before), refiners and people search.
Served: a peer seat with Read on ProductTeam only (manifest copy) gets 403 on my-claude-code's search, and its All Sites search finds the ProductTeam probe but not the my-claude-code one; the System Account finds both; /sites/Search searches All Sites by default. On :5198 "policy" from the Search Center finds the Document Center's policy documents and their approval tasks.
TPL.22SharePoint's Survey "Allow multiple responses? No": a person answers a survey once, through the form and the REST create alike (409 on REST); and a REST create now records its writer as the form does (Author / CreatedBy = the acting principal, not Anonymous)
SurveyResponses.Refusal is the one rule both create doors call before storing (the form's WriteOneItemWithEvents - so the CSV import too - and ListsApiController.CreateItem): on a list of template 102 without AllowMultiResponses="TRUE" (SPList.AllowMultiResponses, a manifest List attribute), a named caller who already has a row (Author or CreatedBy) is refused; the System Account is not limited. Found on the way, by the served test (the lesson of feedback_every_write_door_fires_the_events again): the REST create stamped CreatedBy from User.Identity only, so a seat writing with its Bearer secret was stored as Anonymous and owned nothing - the delete-own-row gate and this rule could not see it. It now stamps the authenticated user, else the acting principal, and Author (SharePoint's Created By) whenever there is a caller. Not added to the part XSD: nothing in a SiteTemplate declares a survey yet, and an attribute nothing reads is the silent-drop class.
Served: spice-verify (Contribute on ProductTeam in a manifest copy) answers TeamSurvey over REST (201), a second answer is 409 "already responded", a third through the form does not land (one row); two anonymous loopback answers both land.
TPL.21SharePoint's Promoted Links Tiles view and any declared view Type by convention: a View Type="Tiles" (or SharePoint's CALENDAR, GRID...) renders XSLT/Views/{Type}.xslt - tiles.xslt and grid.xslt are new files, the C# only stopped special-casing Board and MindMap
The first view added after TPL.20's convention, and it needed no controller code: ListViewModelBuilder now carries any declared non-HTML view Type as ViewType (Board and MindMap keep their spelling), so SharePoint's own types render by file - Tiles (Views/tiles.xslt), CALENDAR (Views/calendar.xslt, already there), GRID (Views/grid.xslt, the datasheet = the Quick Edit grid). tiles.xslt: one tile per row in TileOrder, BackgroundImageLocation as the picture, Title on the tile, Description on hover, LinkLocation as the target (a new tab when LaunchBehavior is "New tab"). The tile URLs are member-written, so only http(s) and site-relative ones are used: a javascript: link renders as #, an image URL with a quote, paren or backslash is dropped. Two traps on the way, both caught by the served test: an XSLT 1.0 predicate on a string variable compiles to a 500 (rewritten as xsl:if), and the heredoc ate a backslash twice (CLAUDE.md's warning - fixed with chr(92), verified with cat -A).
Served: ProductTeam's PromotedLinks list (template 170) opens on its declared Tiles default view; the test posts two tiles out of order and reads them back in TileOrder, the javascript: one as href="#".
TPL.20SharePoint's Survey (102) with its Graphical Summary, and list view stylesheets the SharePoint 2013 on-prem way: defaults are files in XSLT/Views picked up by name (LAYOUTS/XSL), a declared view's XslLink names a custom one - a new view is a file, no code
Operator mid-cycle 2026-10-02: "cant we make it generic that it pics up the xslt in the dir folder, so we dont touch .cs files, less errors? refactor?", "was the _vti thing handy in sharepoint? or factory pattern", "have like default but we can have custom too? the sp 2013 onprem way?". Answer and build: SharePoint 2013 ships its view XSL in the hive (TEMPLATE/LAYOUTS/XSL: main.xsl, vwstyles.xsl) and a view or XsltListViewWebPart overrides it with XslLink - _vti_bin is the web-service folder, not this; no factory needed, the folder is the registry. ListsController lost its view-name ladder: ?view={name} renders XSLT/Views/{name}.xslt when the file exists (a name with anything but letters, digits, - or _ is refused); a declared view renders its XslLink (a path that must stay under XSLT/, else refused) or the default of its Type (Views/board.xslt, Views/mindmap.xslt), else the table. csv, graph, rss and mm keep their C# branches because they change the response (download, feed, another model), not only the stylesheet. The existing projections stay where ten tests and the docs name them; each Views file is a two-line xsl:import of it. View/XslLink (schema.xml, with SharePoint's Default attribute) in ListViewType; ListViewModelBuilder carries it onto the model.
Survey: ContentType.SurveyResponse (a survey is a content type inheriting it, one column per question - Choice, a rating scale as Choice, Boolean Yes/No, Note), ListTemplate.Survey 102 (its own base type, so the content-type fallback never makes custom lists 102), a TeamSurvey on the ProductTeam site, and Views/summary.xslt - SharePoint's Graphical Summary (summary.aspx): per question a bar per choice with count and share, Yes/No for a Boolean, answered-count otherwise; every response counted (a reserved view is not paged). Not built: Allow Multiple Responses (one response per person needs a check in both create doors), branching questions.
Served on :5198: calendar, kanban, quickedit, mindmap and summary render through XSLT/Views, csv still downloads, an unknown ?view= falls back to the table; four responses on TeamSurvey read Too high 2 / About right 2, tools 4 = 50%, Yes 75% (screenshot), left as a demo.
TPL.19SharePoint's Approval Request Change: an approver answers Request Change (optionally Request change from a named person), a change task goes to that person or the initiator with the approver's comments, and completing it gives the approver a fresh task
Closes the last open outcome of TPL.17's Approval. The run state carried on every Workflow Task gains Initiator (the caller who started the run, else the item's Author/CreatedBy) and ReturnTo (on a change task: the approver it returns to). A Request Change answer is not counted as answered (serial order and parallel completion wait for the approver's fresh task); a returning task gets its own row (a short unique key suffix), so the answered task stays in the record instead of being overwritten by an upsert. Field.RequestChangeFrom (SharePoint's "Request change from") on ContentType.WorkflowTask. Served test: alice asks ivan for a change, ivan completes it, alice's fresh task (a new row) approves the document - Approved, _ModerationStatus 0.
TPL.18SharePoint's reusable workflows, associated with parameters: Three-state (Task, Set Field, Task, Set Field) auto-started on new issues by an association whose Data carries the states, Collect Feedback, Approval's Enable Content Approval as a step option; and a REST-created item now fires ItemAdded
Closes SPF.18 (reusable workflows associated with parameters) and SPF.19 (the three-state pattern) the SharePoint way, on TPL.17's task step. New step kind SetField (SharePoint Designer's "Set Field in Current Item"); {Name} in a step reads the initiation form or the association's data, else the item's own column ({AssignedTo}); EnableContentApproval on a Task step (Approval's own option) is the only thing that touches the item's approval status now - Collect Feedback and Three-state leave it alone. EventReceiver gains SharePoint's Receiver Data (Field per name), passed as the run's InitiationData - SPWorkflowAssociation.AssociationData. A run of human and SetField steps ran no phase by design and is no longer reported as failed; when it ends, the item's workflow column reads Completed.
Parts: Workflow.ThreeState with EventReceiver.ThreeState.ProjectTracker (ItemAdded on ContentType.Issue, Data StateField=IssueStatus, Middle=Resolved, Final=Closed - SharePoint's Active, Resolved, Closed on Issue Tracking); Workflow.CollectFeedback with Form.FeedbackStart and a manual association on the Document Center.
Found on the way (served test): the REST create (POST /api/sites/{site}/lists/{list}/items) stored the row and fired NOTHING - no EventReceiver, no listener - so no ItemAdded association ever started for an item made over REST, while the form path did. Root cause fixed with ItemAddedFanout, the twin of ItemUpdatedFanout (the form and the AddListItem op keep their own copies of the fan-out - a later consolidation). Still open: Request Change (back to the initiator, then the approver again).
Served on :5198: a REST-created issue "Login page times out on VPN" assigned to claude-code started Three-state; the seat completed both tasks over MCP (tool_update_list_item) and the issue went Open, Resolved, Closed with Threestate = Completed; Workflow History reads the run start to end.
TPL.17SharePoint's Approval workflow on a durable human task step: Step Kind="Task" files a Workflow Task (0x010801) per approver and parks the run on the task rows; completing the tasks resumes it - in a fresh process too - Serial or Parallel, ending on the first rejection, with the item's Approval column and (on a moderated list) its approval status following
Operator 2026-10-02 approved the new engine seam ("Yes, build it"). Recon: a wiring trace of the engine - WorkflowOrchestrator.RunAsync was one in-memory foreach of Phase steps, no suspend point, no run state beyond Workflow History rows; the existing human loop (ApprovalRequest, VerdictQuorumListener) parks DATA, not a run.
Seam (one): WorkflowStep gains Kind (Phase default | Task), Assignees ({Name} = an initiation-form answer), Assign (SharePoint's "One at a time (serial)" / "All at once (parallel)"), TaskTitle ({Title} = the item's). The orchestrator's Task branch calls WorkflowTasks.Park and returns WorkflowResult.Parked (no Completed history); WorkflowRunRequest gains ListName, RunId and ResumeAtStep, so a resume adopts the parked run id and starts at the next step. The task rows ARE the state: WorkflowInstance, WorkflowAssociation, WorkflowItem, WorkflowStep and WorkflowData (SharePoint's task ExtendedProperties: item, initiation answers, assignees, order). WorkflowTaskListener (Singleton, scope per call, on ItemUpdated AND ItemAdded) hands a Completed task to WorkflowTasks.Completed: Rejected cancels the open siblings and ends the run; Serial files the next approver; all Approved resumes the run (or completes it when the task step is the last). Reassign = change AssignedTo on the open task (no mechanism). Not shipped: Request Change (a task back to the initiator, then the approver again) and the association data for an auto-started Approval (TPL.18).
Parts: ContentType.WorkflowTask (Parent Task; reuses Field.TaskOutcome, Field.Comments and the Workflow History fields), Form.ApprovalStart on ContentType.ApprovalInitiation (Approvers, Assign to, Request, DueDate), Workflow.Approval (one Task step), EventReceiver.Approval.DocumentCenter (WorkflowStart on the Document Center's documents). Workflow History's Event gains SharePoint's TaskCompleted and WorkflowCancelled (SPWorkflowHistoryEventType) - found on the served surface: the first smoke's TaskCompleted rows were refused by the StrictFields choice. The start form now returns to SharePoint's ?Source= (a local path only); it always redirected to ?view=mindmap, wrong for a library; the mind map passes Source.
Discriminating check (advisor): the served test parks a parallel approval, DISPOSES the app, and completes both tasks in a fresh one - the item reads Approved, _ModerationStatus 0, and Workflow History carries WorkflowCompleted under the ORIGINAL run id; a serial run asks one approver at a time and the first Rejected ends it (item Rejected, _ModerationStatus 1, no task for the third). A test-side trap on the way: reading the item in the test's own long-lived scope returned the cached row; the listener writes from its own scope - read in a fresh scope.
Served on :5198: Travel policy 2027 started from the form (alice;bob parallel), completed through the list REST edit (the seat's MCP write was refused - claude-code has Read on DocumentCenter, as it should) - Approved, 0; Expense policy 2027 left mid-run (serial, alice approved, bob's task open) as a live example.
TPL.16Enterprise site templates built the SharePoint way: Document Center (BDR#0), Records Center (OFFILE#1), Blog (BLOG#0 with Posts 301, Comments 302, Categories 303) and the Fantastic 40 Document Library and Review (FAB40#7) and Absence Request (FAB40#0); SharePoint 2019's STS#3 and Communication site in the catalogue; every declared list carries its template number, so the PnP export agrees with Site Contents
Third cycle of the enterprise SharePoint arc (operator 2026-10-02). Correction to TPL.14: its "SiteToPnp no longer fakes 100/101" held only for stamped DeclareLists; the BaseTemplate fallback by content type lives in C# (ListsFor), so a PnP export of the Team Site still wrote 100 for Tasks while Site Contents said 107 - two readers disagreeing (found by the advisor, not by a check). Fixed by stamping TemplateType on every DeclareList whose content type maps to a numbered list template (TeamSite, ProjectSite, MySite, Brainstorm, Helpdesk, Workspace, Intranet) and asserting SiteToPnp over the live TeamSite part (Tasks 107, Calendar 106). spice-edit replace refused MySite and Brainstorm ('caml' is an undeclared prefix - the fragment is parsed without the file root's namespace declarations); those two were stamped by an in-span text patch - owed to HARNESS.17.
Built (reuse-first, no new part kind): DocumentCenter = Documents (101, versioned, EnableModeration) + Announcements + Tasks + Pages; RecordsCenter = DropOffLibrary + RoutingRules (the Content Organizer already shipped) + a moderated Records library - record declaration and holds not modelled; Blog = new Post (0x0110) and Comment (0x0111) content types with PublishedDate, PostCategory (Lookup Categories) and PostTitle (Lookup Posts), ListTemplates Posts 301 and Comments 302; Categories (303) is stamped on the DeclareList only, because a ListTemplate on ContentType.Item would make every custom list fall back to 303. DocumentReview (FAB40#7) = moderated Documents + Tasks + Announcements: content approval IS the review until the human Approval workflow ships - Feature.ApprovalWorkflow is the agent-vote bundle and was deliberately not bound. AbsenceRequest (FAB40#0) = LeaveRequests (HR, reused) + a VacationSchedule calendar. WEBTEMP rows carry SpiceTemplate so the stubs vanish; STS#3 and SITEPAGEPUBLISHING#0 added from the SharePoint 2019 list. Skipped on purpose: DEV, APPCATALOG, GROUP, COMMUNITY (reputation is new engine), EDISC/POLICYCTR/BICenterSite (no compliance engine).
Served: a DocumentCenterPortal blueprint; /sites/DocumentCenter/Lists?format=xml reads Documents 101 (moderated), Announcements 104, Tasks 107, Pages 119 (screenshot); Tool.ListSiteTemplates lists BDR#0, OFFILE#1, BLOG#0, FAB40#0, FAB40#7 as built, 104 left in the catalogue.
TPL.15SharePoint's Calendar (106), Picture Library (109) and Promoted Links (170): the Event, Picture and Promoted Link content types with SharePoint's own column names, their list templates stamped with the template number, and a Calendar on the Team Site as STS#0 has one
Second cycle of the enterprise SharePoint arc (lists first, operator 2026-10-02). Reuse-first: Field.Description reused on all three; Picture is a child of Document, so a picture library is a document library with image columns (the AnyDocAutoAssist receiver matches Document exactly, so it does not fire on pictures). Event: EventDate (Start Time, required), EndDate (Field.EventEndDate, since EndDate as a name was free but the Ids were taken by domain fields), Location, Description, fAllDayEvent, Category with SharePoint's eight choices - EventDate first, so the existing ?view=calendar groups by it with no code. Not modelled: recurrence (fRecurrence/RecurrenceData - enter each occurrence), the Promoted Links tiles web part (the list is a plain list until a Tiles view is needed), picture thumbnails.
Served: /sites/ProductTeam/Lists?format=xml reads Calendar 106; an event posted to /api/sites/ProductTeam/lists/Calendar/items shows under 2026-10-15 09:00 in ?view=calendar (screenshot), then deleted. Live spine: ListTemplate.PictureLibrary 109 on ContentType.Picture (Parent Document), ListTemplate.PromotedLinks 170. ContentTypes.g.cs regenerated (the regen tool reported "already matches" against a stale build - build first).
TPL.14SharePoint's list template number on every list: ListTemplate and DeclareList carry TemplateType (SPListTemplateType), served as SPList.BaseTemplate on Site Contents and Tool.SiteContents and kept by both PnP adapters; Tasks (107) and Contacts (105) list templates, the Contact content type with SharePoint's own columns
Operator 2026-10-02: "forget about the studio. make sure we have enterprise sharepoint functionality with all kinds of templates and wf". Gap by three SP-fidelity researchers (lists vs the ListTemplateType enum from Learn, sites vs Get-SPWebTemplate, OOTB workflows). Order chosen: lists first, then site templates (Document Center, Records Center, Blog, Fab 40 Document Library and Review, Absence Request), then Three-state and Approval; the durable resumable workflow step (serial approvers, Reassign, Request Change) approved by the operator.
Found: no list carried SharePoint's template number - SiteToPnp faked 100/101 from the content type hex, the hive reader reported ListInstance/@TemplateType as dropped, and nothing served it. Now: an optional TemplateType (no default) on ListTemplate and DeclareList; the expander writes it onto the effective List; ListsFor serves BaseTemplate from the list's own TemplateType, else from the one stamped ListTemplate binding its content type (an ambiguous content type gives none). Drop Off Library left unstamped: its content type is Item, so stamping it would make every custom list 101. Stamped: Documents 101, Links 103, Announcements 104, Team Discussion 108, Issues 1100, Site Pages and Wiki Pages 119, Project Tasks 171; new ListTemplate.Tasks 107 and ListTemplate.Contacts 105; ContentType.Contact (0x0106) gets FirstName, FullName, Email (Field.PersonEmail reused), Company, JobTitle, WorkPhone, HomePhone, CellPhone, WorkFax, WorkAddress, WorkCity, WorkState, WorkZip, WorkCountry, WebPage, Comments. onet.xml List/@Type is read too.
Served: /sites/ProductTeam/Lists?format=xml reads Documents 101, Announcements 104, Links 103, TeamDiscussion 108, Tasks 107, Pages 119; Tool.SiteContents on ProjectTracker reads Issues 1100, Tasks 107. PnP round trip keeps 107. Edited with spice-edit; ContentTypes.g.cs regenerated.
HARNESS.17tools/spice-edit: edit the XML spine by part Id as a byte-exact patch, and diff it part by part
Operator 2026-10-02: "why dont you use xquery tool to update add nodes? and maybe xmldiff ... try it, I believe we have our own modif one, otherwise adapt to be a more efficient tool for you". Probed: SaxonCS-HE 13 throws "XQuery Update is not supported in this Saxon Configuration". SPICE own modify path is the ProvisioningApplier (builder ops on an XDocument, PreserveWhitespace) - right for agents and approvals, but a full re-save of a file churns: a no-op round trip of the spine moved 1458 lines (multi-line start tags flattened, " />") and, with NewLineHandling.None, silently turned three prompt attributes holding into spaces - caught by the new part-by-part diff. So spice-edit decides the edit on the XML and writes only that part span (a tag scanner finds it), keeping BOM, line endings and root namespace declarations; round trip insert/text/replace/remove on cycles.xml leaves git diff empty. This row was inserted with it. Owed: an XPath sub-target inside a part (set on a child element), and exposing the same edit + diff to SPICE seats as an MCP tool behind approval.
BRAINSTORM.30Live web parts in the map chat and node notes: a webpart fence renders a real SharePoint web part (ListView) through a fragment endpoint, with the viewer's rights
Follow-up to BRAINSTORM.29's invented Owner column: show the list, not a retyped table. GET _api/webparts/fragment?siteName&listName&type&rowLimit resolves a WebPart through IWebPartRegistry and renders it with WebPartFragment.xslt (imports Page.xslt, so the chrome and body are the page's own). siteName/listName are query values, so ListPermissionGate checks the viewer's read right on the site and list. Only Brainstorm.ChatWebParts types render (ListView): a model writes the request, so never Html. The client reads only the fence's attributes (DOMParser) and inserts the server's HTML. Copilot 1.3.2 is told to prefer a live list over retyping. Served: 3 served tests (render, Html refused 400, unknown list 404); the free model answered "show me the open issues on Projects" with the Issues web part. Owed: the fragment shows the list's DEFAULT view - an "open issues" title over unfiltered rows; pass a view name or a CAML filter (SharePoint's XsltListViewWebPart ViewName); the web-part menu is inert on the map (sharepoint.js not loaded); a Chart web part.
BRAINSTORM.29Rich answers on the mind map: the copilot draws Mermaid diagrams and sortable tables as web-part cards (chat and node notes); the cheap BRAINSTORM.25 items - readOnlyHint read cache, an owner on agent-filed issues, AddFormTemplate
Operator 2026-10-02: "in mindmap view let agent create mermaid and show like webpart widget; also have widget like table display in its chat; do gap what is missing". Gap by three read-only tracers (agent side, map/forms side, chat/widgets): the chat rendered only bold and node chips; mermaid.js was vendored but not loaded on the map; no ContentType.Diagram; web parts render only inside pages (no fragment endpoint, no Chart web part); Tool.MakeWebPart has no Tool part. Shipped the reuse-first slice: renderAnswer splits a mermaid fence into a Diagram card (parsed first with mermaid.parse; a broken one shows its source, marked, never the error graphic; strict security level) and a pipe table into a sortable Table card, in the chat and in a node's notes; one prompt line (Actor.MapCopilot 1.3.1: draw with a fence, quote every label). Owed items: (9) MCP's tool annotation readOnlyHint as an optional Tool attribute (ReadNodes, SearchRows, SiteContents) - an identical call in one answer is served from that run's result; (6b) FileIssue sets AssignedTo from Issues.Owner (the reporter stays in the body - agents are not People rows); (3) AddFormTemplate builder op (checks its ContentType). Fixed on the way: a chat whose remembered conversation was deleted 404ed on every save and lost the transcript - it now starts a new one; the PromptLibrary test now pins only the fallback rules (the Perplexity citation rule of b788a31 had broken it - that commit ran only the briefing tests). Served: on the vacancy funnel the free model drew a 9-node flowchart and a 9-row table; the first run drew mermaid's error graphic (unquoted parentheses in a label) while my check counted it as an svg - the parse-first fallback and the quoting rule fixed it. PM: the model invented an Owner column ("Erik, as indicated in the map") - a table is only as true as its source. Left for BRAINSTORM.25: real web parts in the chat or on a node (fragment endpoint, ListView/Chart), Diagram content type and AnalyseAdopted, the weekly issue triage schedule, review mode for proposals, actor memory review, named MCP profiles, InfoPath rules.
BRAINSTORM.28A brainstorm ends in an outcome: Shape the outcome routes a node to a Project (request form), a New functionality (spec + New Feature) or a New site (an assembly kit) - with a readable wireframe spec first, and PnP Provisioning Template adapters in XSLT both ways
Operator 2026-10-01: "come to a brainstorm outcome at the end like a wireframe template-like outcome to pass further from loose go-with-the-flow to a more deterministic way - project request form, new functionality, new SPICE artifact like a bike assembly but for an SP site"; answers: outcome = all of it (route by type, assembly kit, spec page); kit format: "is delta like a mirror? can we have an adaptor like XSLT? I like all Altova products".
Plan: (1) Workflow.ShapeOutcome on a node with an initiation form (Route: Project | New functionality | New site), its phases writing a wireframe spec (purpose, users, lists and fields, views, flows, features, open questions) as a page for review; (2) per route the deterministic artifact: Project -> Tool.ProposeProject; New functionality -> Tool.FileIssue New Feature with the spec; New site -> an assembly kit = a ProvisioningDelta (AddSiteBlueprint Template=, AddFieldDefinition, AddContentType, AddView, AddWorkflow, AddFeature) parked for approval; (3) the PnP Provisioning Template (pnp:ProvisioningTemplate SiteFields/ContentTypes/Lists/Features) as the exchange format: XSLT 3.0 adapters on the Saxon engine, PnP -> SPICE delta and SPICE site -> PnP, so a kit can be authored, diffed and validated in Altova XMLSpy/MapForce against the PnP XSD (the delta is close to a mirror: same concepts, SPICE vocabulary). Ask before: any new part kind (none foreseen).
Shipped in four commits (9577128 28a, 0cfd5cf 28c, 44ac4c4 28b, ceb4f72 28d), no new part kind. Recon: wiring tracer, SP-fidelity researcher (PnP 2022/09 onto the delta), and a borrow scan of Power Apps Plans, n8n 2.40 and Flowise Agentflow V2. The borrow taken: Power Apps Plans' gated phases (requirements, data, solution, each Looks good / Edit) are SPICE's Pending children plus moderation, so they cost nothing. Deferred: a reject loop with a cap (Flowise Human Input + Loop), declared flow-state keys, a respondedAt stamp on approvals. Flowise being discontinued is unverified.
28a: Tool.TransformXslt sends version 2.0/3.0 stylesheets to Saxon (IAdvancedXmlEngine), and both inputs pass SafeXml.UntrustedSettings first.
28c: Workflow.ShapeOutcome copies the Benchmark set: an initiation form (Outcome, Purpose, Users), then three phases filed as Pending children. A Workflow Step cannot branch, so the outcome is the operator's action on the approved spec, not a branch. Served on the STUDIOS vacancy funnel with the free model: 8 user stories, typed lists (Studio Vacancy, Viewing Slot...) tied to stories, flows and open questions. Defect: the skill was not bound in the department roster (Feature.MapCrew BindSkill).
28b: PnpToDelta and SiteToPnp are XSLT 3.0 files under Config/Adapters/PnP, named by Scope.XsltMappers (a value that is not inline XSLT names a file under Config/). Lists travel as a Feature with DeclareList and views. The round trip is equal on Name, Url and DisplayName, never on ids. Saxon now sets GlobalContextItem, so a top-level variable can read the document.
28d: Workflow.SiteKit turns the approved spec into a PnP template (written by the model) as a Pending child. Tool.ProposeSiteKit maps the approved kit, reuses existing site columns, adds AddSiteBlueprint for a new site, rehearses the kit on an existing site, and parks one approval. File as New Feature (Tool.FileIssue linking the node) sits beside Propose as project on the node menu.
Found only on the served surface: a model's template redefines Title (site-column collision); SharePoint writes CAML Ascending=FALSE where SPICE wants xs:boolean; SPICE's CAML Value types are Text, Number, DateTime and Bool (adapter maps the rest); a dry run cannot see a feature added in the same delta; a heredoc turned a JS '
' into a literal newline - node --check caught it before it broke the map.
PM: three routes work end to end, but value waits on the operator approving the STUDIOS spec and building one real kit (BRAINSTORM.6).
BRAINSTORM.27Sites the SharePoint way and a platform that explains itself: SharePoint's WEBTEMP catalogue and the Fab 40 read as-is, feature stapling by FeatureSiteTemplateAssociation, a self-service site toolkit for agents, actor rows that follow their part's version, and every site and list describing itself
Operator: "add all templates and a toolkit so agents can self-service create sites with template selection, provisioning, feature activation, stapling - can we use [SharePoint's schema] exactly or migrate?; every list a short description and a progressive-disclosure link (why what when who) for agents surfing; a map with a TOC".
Answer on the schema (research, no migration): SPICE already reads real onet.xml and feature.xml/elements.xml as-is (local names); its SiteTemplate/Feature parts stay the runtime truth; what was missing was a reader for the catalogue (WEBTEMP*.XML) and SharePoint's stapling element. SharePoint binds a list to a numeric TemplateType, SPICE to a typed ContentType - kept on purpose.
(a) ActorProfileSeeder does SharePoint's feature upgrade: a row records the part Version that seeded it; a higher part Version re-applies the part's fields (other row fields kept) - served: "1 seeded, 1 upgraded" (Actor.SiteBuilder new, Actor.MapCopilot 1.0.0 -> 1.2.0); the hand edits of the Hub/Actors row are no longer needed.
(b) Config/TEMPLATE/1033/XML/WEBTEMP.XML (the 72 SharePoint 2016 web templates with titles and descriptions, from the Get-SPWebTemplate list; numeric template IDs left out rather than guessed) and WEBTEMPFAB40.XML (the Fab 40), read by WebTempContributor into Hidden, list-less catalogue SiteTemplates; templates SPICE builds carry WebTemplate (Team Site STS#0, Enterprise Wiki ENTERWIKI#0, My Site SPSPERS#0, Project Site PROJECTSITE#0, Product Catalog PRODUCTCATALOG#0, Helpdesk and Inventory from the Fab 40).
(c) Feature stapling: FeatureSiteTemplateAssociation (Id, TemplateName = template id or key) on a Feature part, an AddFeature builder op, one resolver (TemplateFeatures) for the boot stapling pass and - new - for a site created at run time from a template. Agent toolkit: Tool.ListSiteTemplates (picker; catalogue=true adds WEBTEMP and the Fab 40), Tool.RequestSite takes a key, Tool.RequestFeatureActivation and Tool.RequestFeatureStapling park for approval (the RequestSite path); Skill.SiteProvisioning, Actor.SiteBuilder in Agency.MapCrew, bound by Feature.MapCrew. Served: picker lists built templates and counts the catalogue (Fab 40 filter 38 = 40 minus 2 built); both requests parked and their deltas pass a dry run (AddFeature Feature.Staple.mapcrew.teamsite; ActivateFeature binds the crew skills); test requests deleted.
(d) Every manifest List (190), SiteBlueprint (63) and DeclareList (47+) has a one-line Description: written by a worktree agent, but its worktree started from an old base (0a567ca), so a merge would have rolled back the day's work - the merge was aborted and the 284 descriptions were ported by element identity onto today's files, 20 newer ones written by hand. Site Contents shows the site's and each list's description with an About link (List Settings) and serves ?format=xml; list pages show About this list and Site contents under the description; Tool.SiteContents gives agents the same. Also reverted: boot churn that commit 9b4a579 had swept into the manifest. Suite 4297/4297.
Lesson: a worktree agent's branch must be merged only when it started from the current HEAD - check git merge-base first.
BRAINSTORM.26An agent chat that keeps its word: one context rule (approved and own content in, proposals out), stuck agents file Bug, Improvement, New Feature or Feedback on the backlog, conversations kept as Discussion Board rows, answers with their steps and Copy, Retry, Not helpful, Report a problem - and a local model's text tool calls are run, not shown
Operator: "features a good agent chat has; what do I approve exactly that is taken in context; is MCP better for small scoped agents; let the agent request bug, feedback, suggestion, new tool so when stuck we do something with it".
(a) Measured first: approval barely gated context - ReadNodes and SearchRows returned Pending proposals, workflows got the whole subtree, only ProposeProject used approved children. One rule now: what the person wrote or approved is content; a Pending proposal is counted ("N proposals waiting - not facts, do not propose again") but not read, searched or handed to a workflow; Rejected only feeds "do not propose again". The chat states it: what is selected, which node is the context, that proposals are left out. Served: the Templates node reads "proposals waiting for approval: 10" with no proposal listed as a child.
(b) Tool.FileIssue + Skill.ReportProblems on the copilot (Feature.MapCrew binds it): Bug / Improvement / New Feature / Feedback on /sites/Projects/Lists/Issues (Field.IssueType gained Jira's Improvement and New Feature and JSM's Feedback; where it files is Scope.Brainstorm data); an open issue with the same title is not filed twice. Served: asked to email a branch as PDF, the copilot read the branch, summarised it, filed "PDF Generation and Email Sending Capabilities" as a New Feature and said so (test issue deleted). Answers carry Not helpful (Feedback) and Report a problem (Bug) that file the Q, A and steps.
(c) Chat: conversations are rows on a new Brainstorm Conversations list (ContentType.DiscussionMessage - SharePoint's Discussion Board message; Title = first question, Body = transcript), restored on reload, New starts another, History opens the list; tool_chat_agent trace=true ends a reply with "[steps] Tool.ReadNodes x2, Tool.FileIssue", shown under the answer; Copy and Retry; the last 4 exchanges ride along. Browser-proven: ask, steps, buttons, reload restores the conversation; test conversation deleted.
(d) A live failure fixed at the provider: the local Qwen-class model wrote its call as text ("<function=Tool_ReadNodes><parameter=ids>...") and the gateway passed it as content, so the chat showed the call as its answer. OpenAiToolTranslator.RecoverTextToolCalls lifts Qwen/Hermes XML and <tool_call>{json} calls into real tool_calls before history and translation (phases benefit too); tools/toolloop-smoke.ps1 PASS. The Hub/Actors row of the copilot was synced by hand again (the planned drift fix).
MCP answer recorded for the operator: inside SPICE an agent's tools are already scoped by its SkillCatalog (the copilot sees 4 tools), so MCP adds nothing there; MCP is the door for outside agents, where a scoped profile per agent kind would help a small model (planned). Suite 4293/4293.
BRAINSTORM.24The workplace: chat on native tool calling, trustworthy answers saved as comments, the context from the nearest declaring node, specific suggestions, notes and discussion in the panel, hide proposals, focus on a branch, a full-width map - and the map's script out of the XSLT
Operator: "what would you improve, add, refactor - see the chat log - improve the workplace". Evidence: the screenshot (about 30 dashed AI proposals, mostly generic: Template Governance Policies, Template Version Control; a 1200px column with dead space; a side panel showing only title and path), the saved "Answer:" node whose body was raw tool-call text with four invented node ids, the copilot's own memory note blaming the operator for a node id it had invented, and an in-memory chat bus that loses every conversation on restart.
(a) Refactor: the map's 656-line script moved from MindMap.xslt into /js/mindmap.js (the stylesheet emits data attributes only; node --check runs on the real file) - three heredoc-escape breaks in one day were the cost of keeping it inside XSLT. (b) Root cause of the bad chat: IsEchoLoop matched "echo" anywhere in the failover chain's name ("litellm (+anthropic (+echo fallback) fallback)"), so every agentic chat ran the ReAct text protocol, where the free model wrote tool calls as prose that the loop took for the answer. Only the primary decides now; served: a question about a node came back grounded with only real cited ids. Two misleading self-written memory notes deleted (recycle bin). (c) ProjectContext is the nearest node that declares a context (Sources or Comments), not the top of the map, whose placeholder note had been prefixed to every read. (d) Suggest ideas gets that context and a sharper instruction (Setting + byte-equal fallback): served under a probe context the ideas named Ghent University, Vaartstraat cafes and geo-targeted ads instead of generic management topics. (e) A chat answer saves to the node's Comments (append-only) without a reload, and a reply that is only tool-call text cannot be saved; the panel shows the selected node's notes and discussion with a comment box - served: the comment came back stamped with author and time. (f) Hide proposals (N), Focus on this branch (?focus=ID, the branch laid out as the map, a link back), and a full-width map with a 380px panel - browser-proven with no console errors. Probe rows deleted. The operator's own "Answer: add all site templates..." node (raw tool-call body) is left for them to delete. Suite 4282/4282.
BRAINSTORM.23Brainstorm with context, InfoPath forms and a benchmark start: box selection, workflow status on the node, a root node as the project context, FormTemplate (XPath 3.1 rules on Saxon) as a workflow initiation form, Workflow.Benchmark, and a map copilot that can add
Operator: box selection; a Claude-Project-like context; start a brainstorm from similar products and pick features; specialist advice; presentation or Mermaid; InfoPath copied fully the SPICE way; see a workflow running on a node; keep it declarative. Answers: InfoPath all, improved the SPICE way; context where most useful; specialists reuse-update-generate; outputs Mermaid, deck, feature table and other known ones.
(a) Box selection on empty canvas (Shift/Ctrl adds) - browser-proven: a box over 5 nodes picked 8. (b) Workflow status column: Hub/WorkflowHistory folded per item and workflow into a WorkflowStatus block, a pulsing chip on the node (Pre-mortem 0/3 .. 3/3, Completed), the map refreshes every 20 s while one runs - served on a probe. (c) ?view=mermaid shipped then reverted on the operator's question: a conversion of the same tree adds nothing; Mermaid earns its place as content (a Diagram content type written by workflows, planned). (d) Context on the root node: Field.V3Comments (SharePoint's append-only Comments) and Field.SearchScope (Sources); ProjectContext walks ParentID to the root; ReadNodes and every manual workflow lead with it; an ambient SearchScope (tool_chat_agent scope=, the workflow run) makes Tool.SearchRows search the Sources - served through ReadNodes; the chat-to-SearchRows scope is unit-tested, not proven through a model.
(e) FormTemplate (new part kind, approved): ContentType is the schema, Validation/ErrorCondition is xsf:errorCondition in XPath 3.1 - first written as XPath 1.0 and switched to the platform's Saxon engine (IAdvancedXmlEngine) at the operator's word; the rule runs as /myFields/Field[boolean((expr))]; the browser posts the answers to a Validate route so one engine decides; no engine fails closed. Workflow InitiationForm (SP 2013 InitiationUrl); InitiationData flows WorkflowRunRequest -> PhaseRequest -> BriefingRequest as {{init.Name}} (the token pattern now allows dots) and as FORM ANSWERS in the task (phase Goal text is never token-substituted, so no behaviour change). FormView renders it (Action, SubmitLabel; plain post so a refusal shows its message). Served: GET form, Validate -> [BenchCount: Compare between 1 and 10 products], refused POST re-rendered with the message, valid POST 302 to the map, Workflow.Benchmark compared exactly 3 products from real web results and wrote 6 features as Pending children plus a choosing guide written for the typed goal; probes deleted. Kept from InfoPath: initiation form, one XML payload, errorCondition; dropped: code-behind, SOAP data connections, per-form myschema.xsd/view1.xsl, .xsn packaging.
(g) The map copilot could only read, so "add all known site collection templates under SharePoint" searched until it ran out of turns. Tool.AddNodes (UpdateListItems batch of New, Pending, duplicates skipped) + Skill.MapEdit; the copilot's Goal says to add from its own knowledge for well-known items. The live actor is its Hub/Actors row, seeded once and never refreshed from the part - the row was updated by hand (drift, see the planned row). A loop that runs out of turns now says the last step it completed. Served: the copilot added the templates in one call; the free model also invented some (Help Desk Site, Training Site) - test nodes deleted. Suite 4280/4280.
BRAINSTORM.22The mind map as orchestrator: brainstorm funnelled to projects (Stage + Propose as project behind one approval), workflows inherited down the content-type chain, the map as a declared default view, a second method (Pre-mortem), and the parts builders can emit ContentType, Workflow, Phase, EventReceiver and View
Operator: "get me aware and in control ... transformation and delegate, more default workflows and inherit them, default views like the mind map, parts builders, brainstorm sessions funnelled to projects, harden the way of thinking and reuse patterns". Answers: funnel = both (a visible Stage and a Propose tool through approval), inherit by default, all four tracks.
(a) Inheritance, SharePoint's association pushdown: an EventReceiver on a content type now also fires for its descendants (Parent chain, cycle-safe), and the node menu's Workflows block honours it; Inherit="false" (XSD boolean, default true) is the opt-out. Measured first: 13 receivers carry a ContentType, 3 would newly reach child types - ER.OnRequestDispatchedToGenesis (Department-filtered, harmless, inherits), ER.OnIncomingRequestAdded (would route PurchaseRequisition and CaravanTyreRequisition into the front desk) and ER.AnyDocAutoAssist (would AI-assist SourceDocument, ADRs and decks) - the last two set Inherit="false".
(b) The demand funnel (Project Server demand management): Field.Stage (Idea, Proposal, Project, Parked; Default Idea) on MindMapNode, shown as the node's first chip; Tool.ProposeProject (deterministic) builds ONE delta - project header (Status Planning, client and currency from Scope.Brainstorm Funnel.*), one ProjectTask per approved direct child in map order, the node's back-link (ProjectRef, Stage Project) - and parks it as an ApprovalRequest on Engineering (the ExtractSkill/SelfReview park), node to Stage Proposal at once; "Propose as project" on the node menu. Served end to end on a zz- node: propose, operator Approve verdict, quorum flip, ApprovedDeltaApplier applied - ZZFUN2 with ZZFUN2.1/.2 on /sites/Projects, the node at Stage Project; probes deleted.
Two bugs the smoke found: (1) the quorum flip copies the stored request back through AddListItem, and the engine columns AddListItem itself stamps (ContentType since HARNESS.6, ID since BRAINSTORM.4b, moderation) failed validation on the StrictFields ApprovalRequest - so NO verdict-approved delta could apply since then. Fixed at the root: AddListItem validates without its own engine keys; regression test. (2) A delta key without "item:" commits a row no reader sees (the convention ExtractSkill documented); the tool prefixes. The first probe left five such unprefixed rows (Projects/Projects ZZFUNNEL, ZZDRY; ProjectTasks ZZFUNNEL.1, ZZFUNNEL.2, ZZDRY.1) that no tool can delete (every delete path prefixes) - invisible, harmless, to purge from storage by hand.
(c) Delegation: a task on ProjectTasks assigned to a seat is filed on its My Site by the existing listener - the funnel ends there, no new code.
(d) Methodology library: Pre-mortem (Gary Klein, HBR 2007) as data beside IOODARI - KB.Method.PreMortem, three phases (Failure, Causes, Mitigate) each filing one Pending child, Workflow.PreMortem, a WorkflowStart receiver on MindMapNode. Served generation probe: three Pending children on the free model.
(e) Merged from worktree agents: declared MindMap views (View Type="MindMap" Url="Map.aspx", default on Ideas and AgentMap; served - the list root is the map, ?view=AllItems.aspx the table) and builder op handlers (AddFieldDefinition, AddContentType, AddPhase, AddWorkflow, AddEventReceiver, AddView) that run the boot pipeline on a candidate spine and refuse outside an approved delta. Manual KB.Manual.11 and the seat skill document the funnel, the methods, inheritance and the views. Suite 4264/4264.
BRAINSTORM.21Merged and served: document viewers the WOPI way (BRAINSTORM.12) and folder assimilation by content sources (BRAINSTORM.9); document upload repaired - its form never carried the antiforgery token
Built in parallel by two worktree agents and merged (73ec6a1 viewers, bd2a1ea content sources; one keep-both conflict each in the Brainstorm template and the ledger). Served smoke, which the agents could not run: (a) folder assimilation - the seven approved roots are seeded with Include=false (nothing crawled until the operator ticks one); a throwaway source over the repo plans folder crawled 3 folder nodes onto Ideas and a second crawl upserted 0 (the BRAINSTORM.19 unchanged-skip at work); a seat's crawl lands Pending on the moderated list, a schedule's lands as the System Account; probe rows deleted. (b) viewers - the first real upload returned 400: DocumentController's upload form never rendered the antiforgery token its [ValidateAntiForgeryToken] POST requires, so uploading a document was broken for everyone (pre-existing, unnoticed because no test uploads). The form now issues the token (IAntiforgery.GetAndStoreTokens) as the hidden field; DocumentUploadFormServedTests pins it. Then a .md previewed as rendered Markdown with an embedded script escaped, a .csv as a table; test documents deleted. Known limits carried from the agents: uploaded files do not appear in the Lists/{lib} view (pre-existing), the preview has the same (absent) permission check as View and Download, Markdown link URLs are not sanitised (same as the wiki renderer), an upsert never deletes a folder removed on disk. Suite 4240/4240.
BRAINSTORM.9Folders onto the mind map, the SharePoint Search way: a Content Sources list on the Brainstorm site (start address, content source type, Include tick, crawl depth), crawl rules as Settings, one IContentSourceProvider per kind, Tool.CrawlContentSources upserting one node per folder under a root per source
Operator-approved roots seeded as ContentSources rows with Include=false (OneDrive, NextCloud, repos in the profile / D:/Projects / D:/git, the whole profile, D:). Providers: FileShare (folders, last write, README first lines only), GitRepository (plus git log -1; degrades without git), OneDrive and NextCloud as FileShare over their local sync folders. Safety: excludes (Scope.Brainstorm Crawl.ExcludePatterns) neither read nor descended; hidden/system folders, links and junctions skipped; cloud placeholder READMEs never opened; Crawl.MaxItems cap per source; depth clamped to 5; the tool takes no path argument. Keys cs-{source}-{sha256(path)[..12]} so a re-crawl merges via SourceHarvester.UpsertRows; Status left to the Task default so the operator's Status survives. Upsert never deletes: a folder removed on disk keeps its node. Not booted here (the :5198 LAN task owns the port): the served smoke is owed - GET /sites/Brainstorm/Lists/ContentSources shows 7 rows, tick one, invoke the tool, open ?view=mindmap. Suite 4213/4214 in the worktree (V223 skill test skips paths containing "worktrees" - environmental).
BRAINSTORM.20The crew kit is a SharePoint Feature (Feature.MapCrew), a second map draws the agent landscape (AgentMap, tools/map-agents.py), and the seat's own skill spice-mindmap is published in SPICE
Operator asked for a correct name for a "profile/context/crew set or kit": SharePoint's word for a deployable bundle a site switches on is a Feature, and SPICE already uses it for exactly this (Feature.VentureCrew "staffs the Venture Engine division with its competencies"). Feature.MapCrew binds Skill.MapResearch and Skill.Ioodari; the Brainstorm blueprint activates it - served: "Roster for Brainstorm: 2 own skill(s) (incl. 2 from Features)", replacing the earlier "not seated ... binding on competency" warning. The kit = Feature.MapCrew + Agency.MapCrew + the two actors + the tools + KB.Method.Ioodari + the two setting scopes + KB.Manual.11 (now describes the kit, the maps and the seat skill). Agent landscape (the operator's "map out how we use agents"): a second list AgentMap on the Brainstorm template and tools/map-agents.py, which reads the spine with XQuery (tool_xquery) and upserts agency, member, skill and tool nodes with stable keys plus an "Actors in no crew" branch by department - served: 260 nodes, a re-run stable, ?view=mindmap renders all 260. The seat skill spice-mindmap (.claude/skills) holds the commands (read, ask the crew, write, suggest, IOODARI, .mm, map a topic, rules) and is published to my-claude-code/AgentAssets Skills/spice-mindmap/SKILL.md, read back by query_my_skills. MCP: every capability is already an MCP tool on /mcp/jsonrpc, so no new server; no hook added (nothing needs to run on its own yet). Suite 4211/4211.
BRAINSTORM.19Owed fixes: the IOODARI phases read the method's own knowledge (not the whole knowledge base), an upsert leaves an unchanged row alone, a .mm round trip keeps multi-line notes
Commit 4981a19. (1) Why IOODARI drifted into platform jargon on a balcony-garden node: BriefingBuilder gives a phase that declares no knowledge query EVERY KnowledgeArticle it is cleared for - the whole platform knowledge base - and a small model latches onto it. The method is now data, KB.Method.Ioodari (tag ioodari, moved out of the chat skill), and each phase declares UsesQuery Query.KnowledgeByTag Args=tag=ioodari. Served on the same probe: before, standard garden credits from KB.CreditsAndImprovementDeck; after, container gardening with dwarf varieties, 10-15 L pots with drainage, a balcony weight check, an 80 percent survival target - on topic, still inventing a citation id now and then (small model). (2) SourceHarvester.UpsertRows skips a row whose supplied fields already hold their values (SharePoint versions only a real change); ID and ContentType are ignored in that comparison because the platform stamps them and an update never writes them (ItemUpdateService.PlatformStamped) - the first version of the check called 47 legacy rows changed forever. Served: re-importing an unchanged map export wrote 0 of 81. (3) The .mm round trip flattened multi-line notes (normalize-space): the export now writes one p per line and the import joins them with newlines. Honest note: the diagnostic re-imports before (3) flattened a few multi-line bodies (the pending suggestions' WHY plus Suggested-by lines); the text is intact and the prior versions are in each item's history.
BRAINSTORM.18The mind map handed to the platform's builders as a service: Agency.MapCrew (entry: the map copilot), two Service Catalog rows, the Ship's Manual page KB.Manual.11, requests routed by the existing front desk
CONFIG-ONLY hand-over, per a wiring trace: an Agency answers through its Entry (ChatAgentToolInvoker.ResolveSpeaker), the Service Catalog is Reception/ServiceCatalog read by query_service_catalog and by Phase.DispatchRequest, the Ship's Manual is KnowledgeArticles tagged manual (reached by every seat's briefing through Query.KnowledgeByTag). Added: Agency.MapCrew (Entry Actor.MapCopilot, members MapCopilot and MethodCoach), catalog rows mind-map-chat (Tool.ChatAgent) and mind-map-ioodari (Workflow.Ioodari), KB.Manual.11-MindMapCrew (ask, suggest, run a methodology, read/write nodes, request changes). Served as a builder seat would: query_service_catalog lists both rows, /board/manual shows the page, tool_chat_agent to=Agency.MapCrew from=claude-code answered with real cited ids after reading the node (detail=full) and its seven children (summary) - progressive disclosure seen in the log - and a Reception/Requests row with ServiceRef=mind-map-chat was routed by the front desk to IncomingRequestTarget=Agency.MapCrew (probe deleted). The crew's first answers exposed three more small-model argument shapes, each confirmed by a direct call before the fix and now pinned in MapChatToolsTests: ids in the citation form [[node:ID]], ids as a JSON array string, and list given as Site/List; ReadNodes now logs the arguments it received (that log is what found the JSON-array case). Suite 4210/4210.
BRAINSTORM.17Chat with the selection: a panel beside the mind map - selected or dragged nodes become context chips, the map copilot answers on the free model with progressive disclosure (Tool.ReadNodes, Tool.SearchRows) and cites nodes that select themselves on the map
Borrowed, by a scout, from Heptabase (chat with selected cards), Obsidian Copilot (add-to-context chips), M365 Copilot and Notion (answers cite items) and Anthropic's contextual retrieval / progressive disclosure (send a breadcrumb path with every item; open more only on demand). Reuse, by a wiring tracer: tool_chat_agent already runs the free tool loop over MCP from the browser and takes its tools from the actor's skills, so the copilot is DATA - Actor.MapCopilot (free model, Goal holds the citation rule) and Skill.MapResearch (RequiresTool). New C#, two tools only, because nothing read rows: Tool.ReadNodes (summary = title, path, status, note, parent, children and links by id; full = every column; an id alone reads MapChat.DefaultList - the free model dropped site/list and looped on the error until that default existed) and Tool.SearchRows (Tool.Search covers knowledge articles, not rows; this searches the lists MapChat.SearchLists names - SharePoint search's result source as a Setting - and ranks by matched terms; rejected proposals are not content). The panel: chips with a size readout, Ask about selection, drag nodes onto the chat (it highlights; a drop is context, never a move), the last two turns as history, answers rendered with [[node:ID]] as chips that select and reveal the node, unknown ids shown as unverified, Add to map under the context node. The free model trims id prefixes (idea-d450fd91b4 cited as d450fd91b4): a citation resolves only when exactly one map id ends with it, so it stays verified. Served (Playwright on :5198): two nodes into context (~45 tokens), a question answered in seconds citing real proposals, a cited chip selected its node, a drag landed as a chip and the node kept its parent; free gateway only (paid 0). Owed: answer quality on the small local model is uneven; no streaming; tool_chat_agent's tool loop is a shared singleton (concurrent chats can interfere). Suite 4210/4210.