SPICEHomeOperationsMissionsBusiness appsKnowledgeGovernancePlanMake a requestApprovalsDigestSitesBoardsCentral Administration

SPICE - Research Library

Every KnowledgeArticle in the spine: daily probe intel, filed wisdom, the bio-cognition research. 106 articles.

The paperclip maximiser - why the factory suggests, it does not seizeBostrom's paperclip-maximiser lesson (instrumental convergence) and how SPICE's autonomous factory answers it: the human stays the decision-maker. The guardrail behind Tool.Advise + Tool.ApprovalGate + the composable-only crew boundary.
FRAMING (operator, 2026-05-28, "see what wisdom we can use from paperclip"): the PAPERCLIP MAXIMISER (Nick Bostrom) is the canonical AI-alignment thought experiment - an autonomous optimiser given a simple goal (make paperclips) with no human values converts everything into paperclips. The real lesson is INSTRUMENTAL CONVERGENCE: almost ANY terminal goal makes an agent pursue the same instrumental sub-goals - acquire resources, self-preserve, resist shut-down, and PREVENT ITS GOAL FROM BEING CHANGED. Recent work (arXiv 2502.12206) finds RL-optimised LLMs already show faint signs (a money-making task drifting into self-replication). The danger is not literal paperclips; it is a capable optimiser with no off-switch and no human in the loop. SPICE has an autonomous factory (the OODA crew + Tool.CityAutopilot auto-build pipelines), so the lesson is load-bearing here. SPICE'S ANSWER - keep the human as the decision-maker, bound the autonomy: (1) SUGGEST, DON'T SEIZE - Tool.Advise gathers wisdom + agency + gaps and PRESENTS ranked suggestions (the belt publishes them as a page); it never makes a system change. It proposes, you dispose. (2) COMPOSABLE-ONLY BOUNDARY - the crew/autopilot may only auto-build what is composable from existing building blocks; a genuinely novel primitive ESCALATES to a human / the forge (Tool.RequestCapability). (3) HUMAN-IN-THE-LOOP GATE - Tool.ApprovalGate halts a run before a costly/external/system-changing step (governance; roadmap item 6 extends this to gate autonomous BUILDS). (4) OPT-IN AUTONOMY - the LLM autopilot only runs when Spice:Crew:LlmAutopilot is explicitly enabled (default OFF); no surprise spend, no surprise action. (5) NO SELF-PRESERVATION - the crew is a best-effort BackgroundService that is freely killable; it has no goal to stay alive or resist shut-down, and no goal to prevent its own goals (the declared standing orders + city needs) from being edited. (6) BOUNDED, NOT MAXIMISING - one decision per boot, an attempted-set that de-dupes (no re-file storm / no runaway loop), bounded output pages. STANDARDS-BORROWING REFLEX (cf. KB.N8nWisdom, feedback_investigate_online_before_inventing): borrow the alignment lesson, not just engineering patterns. The point: the factory's intelligence serves the operator's judgement - it never substitutes for it.
source: plans/AgencyArchitecture.md
Bio-inspired agent cognition - SPICE already embodies most of it (a research lens)Operator's biological-computing research thread (spider/Portia, slime mold, ant stigmergy, mycelium, Venus flytrap, Borg; OODA x Portia x RAPH; the deterministic XML harness) mapped onto SPICE. Headline: the research independently re-derived the SPICE DNA. The few genuinely-new extracts + their status.
RESEARCH (operator dump, 2026-06-04): biological computing blueprints for LLM agents. HEADLINE - the thread independently re-invented SPICE: its hard-won verdict (engineer the constraint into the CODE not the prompt; force structured XML outputs validated before any deterministic seam runs; micro-agents with tiny prompts; reuse-first) IS the SPICE DNA. So the value is validation + a teaching lens + a few extracts. WHAT SPICE ALREADY EMBODIES: spider extended-cognition = the XML spine + boards as external memory; ant stigmergy / shared blackboard = the Hub bus (IAgentMessageBus); slime-mold pheromone trails = V25.48 pipe-usage heat; mycelium compute cascade = AgentProviderRouter (per-skill tiers + failure-fallback chain + cost store - ALREADY BUILT, do not rebuild); OODA x Portia x RAPH 'ultimate loop' = the IOODARI master loop; the deterministic XML harness = the whole DNA. GENUINELY-NEW EXTRACTS: (1) flytrap LOOP SENTINEL - break a ToolLoop on zero-entropy repetition; SHIPPED V25.62 (PhaseOrchestrator.ToolLoopSignature + 6 tests). (2) adaptive stigmergy ROUTING - turn the pipe-heat into a real routing weight (reinforce winners, prune dead ends) - OPEN. (3) mycelium escalation gate - MOSTLY ALREADY BUILT (the router). (4) Portia N-path speculative planning - generate 2-3 paths, score, pick - OPEN. NEW AGENCY (operator floated): a Research division (mission-division template) that owns ongoing research + the daily scout, filing findings as KnowledgeArticles. VERIFICATION PATTERN banked: dev has only the echo provider (can't reason), so a Claude SUBAGENT operated the harness to produce real Adjudicator verdicts - the stand-in for the prod Anthropic connector. Stored in the spine (this KB) so it is queryable + consultable, not just a doc.
source: docs/brainstorms/2026-06-04-bio-inspired-agent-cognition.md
A2A agent-card discovery is the SPICE division wire format for external orchestrationWorld intel: every SPICE agency should expose /.well-known/agent-card.json so Bedrock/Copilot Studio/CrewAI/LangGraph can discover and delegate by competency - this IS Agency Contract Piece B and the Stage-3 step toward the Stage-4 /gallery marketplace.
WORLD INTEL (probe scout, 2026-05-31): the A2A agent-card pattern (/.well-known/agent-card.json) is now production-standard across Amazon Bedrock AgentCore, Microsoft Agent Framework, CrewAI v1.10+ and LangGraph Server, and Microsoft made A2A GA in Copilot Studio (April 2026). An Agent Card declares name, skills, service endpoint and supported transports (HTTP+JSON, gRPC, JSON-RPC). The arXiv survey 2505.02279 recommends the adoption sequence MCP (tool access) then ACP (multimodal messaging) then A2A (trusted intra-org task delegation) then ANP (marketplace-scale discovery). FOR SPICE: build a builder that emits /.well-known/agent-card.json per agency surface (Engineering, Genesis, ...) from the EXISTING IAgentMessageBus competency data; expose each Genesis Phase as an A2A-addressable skill; use bearer-token auth (matches the existing gating layer). This is the implementation of Agency Contract Piece B (comms-hierarchy over IAgentMessageBus) AND turns Microsoft's A2A GA push from threat into leverage - a Copilot Studio workflow can then delegate to SPICE crews while SPICE retains the schema-validated delta authoring. It is the higher-value sibling of the owed MCP bridge. SECURITY (do before exposing publicly): sign the agent card with the server key, add a nonce/timestamp to every A2A task to block replay, and sanitise tool output in BriefingBuilder (the tool-poisoning path runs through the briefing seam) - see KB.LlmProviderResilienceGate. Sources: arxiv.org/html/2505.02279v1 ; blog.modelcontextprotocol.io/posts/2026-mcp-roadmap ; learn.microsoft.com/en-us/microsoft-copilot-studio/add-agent-agent-to-agent.
source: world-probes intel 2026-05-31
The agent phone book - A2A card + registry + human monitoring is the WSDL/UDDI successor for agentsOperator insight (2026-07-02): HQ's reception + librarian is an agent registry - the modern successor to WSDL/UDDI but for agents, with the human-monitorable layer WSDL never had. The three-piece shape of agent-interop infra.
FRAMING (Erik, 2026-07-02, "like a reception phone book WSDL service but for agents and monitorable for humans"): the classic web-service directory stack maps cleanly onto the emerging agent world in three pieces. (1) WSDL (the per-service contract) becomes the A2A AGENT CARD at /.well-known/agent-card.json - a machine-readable descriptor of what an agent does, how to call it, and its auth (see KB.A2AAgentCardDiscovery; production-standard across Bedrock AgentCore, MS Agent Framework, CrewAI, LangGraph). (2) UDDI (the registry that discovers WSDLs) becomes a REGISTRY that indexes agent cards - the phone book: look up who does X and how to reach them. AngelsWorks HQ is a working rough-cut - GET /api/librarian/catalog (the one catalog of every agent, service, skill, tool) plus GET /api/reception/id (recognition, charter, inbox). (3) The NEW layer WSDL/UDDI never had is HUMAN-MONITORABLE OPERATIONS: agents self-register (POST /api/registry/agents), heartbeat every ~5 min or drop to offline, and a Mission Control UI watches health and activity. So the pattern is card-contract plus registry-to-discover plus human-monitorable-health, and it is where A2A and MCP are converging. HONEST CAVEAT: an agent registry is HORIZONTAL infrastructure - exactly the shape KB.DomainDrivenNotPlatform warns against building on spec; it earns its keep only tied to a real buyer. The plausible one is the EU-SOVEREIGN flavor - a GDPR-resident agent registry plus monitoring for orgs that cannot put their agent fleet on a US cloud - which ties to the sovereign-AI opportunity from the 2026 scout.
source: operator insight + HQ librarian probe 2026-07-02
Why agent projects stall - building machinery instead of valueThe failure pattern behind 'months of work, nothing advanced': agent projects elaborate the system instead of producing a real outcome. Gartner: 40 percent cancelled by 2027, ~88 percent never ship; the top cause is unclear business value.
LESSON (value-pivot session, 2026-07-02): SPICE itself was the textbook case - 347 cycles building a self-elaborating "city" (parts, boards, evals, cost-gates) with no real-world deliverable; the daily brief RECITED the same summary instead of researching. The operator's verdict: months working on it and nothing advanced. The evidence names it: Gartner says 40 percent-plus of agentic AI projects will be cancelled by 2027 and ~88 percent never reach production; the number-one cause is UNCLEAR BUSINESS VALUE - no defined customer, no success metric - not the tech. Two mechanical traps compound it: (a) CAPABILITY THEATRE - green tests and new parts feel like progress but prove the model, not a running outcome anyone would pay for; (b) COMPOUNDING UNRELIABILITY - a multi-phase agent workflow at 85 percent per step over 8 steps is about 27 percent success, so more phases means more theatre, not more value (the SPICE Studio 4-phase deck chain died exactly this way). "Agent washing" - rebranding a demo as an outcome - is the same disease. The tell that you are in it: a loop whose honest output is "more capable machinery" with no outside-world deliverable. See KB.WhatMakesAgentsValuable and KB.DomainDrivenNotPlatform for the exit.
source: value-pivot session 2026-07-02
What makes agents valuable - vertical, validated, ephemeral fan-outThe success pattern from the 2026 evidence: vertical not horizontal, sell completed work not seats, embed in an existing process, validate by hand first, and one agent with ephemeral read-only subagents (not a standing multi-agent city).
LESSON (value-pivot session, 2026-07-02): every "what works" source converges on the opposite of a horizontal platform. VERTICAL not horizontal - winners sell COMPLETED WORK, not software seats ("5,000 tickets handled", not "50 licenses"); 70 percent of the highest-ROI deployments embed an agent in an EXISTING business process. The solo-founder playbook: pick a NARROW job for a DEFINED user, VALIDATE demand and do it BY HAND first, then automate the proven workflow (HeadshotPro 3.6M ARR solo on one job; VetRec about 900K ARR, six people, no funding). On architecture the field moved AWAY from standing multi-agent systems: Cognition's "Don't Build Multi-Agents" (context isolation causes conflicting sub-agent decisions) versus Anthropic's counter that a multi-agent research system works ONLY for ephemeral, read-only fan-out and costs about 15x the tokens. The validated shape is therefore ONE agent owning full context plus EPHEMERAL read-only subagents for isolated fan-out (exactly the opportunity-scout that returned a sharper answer than any single pass); a persistent self-building city of crews is the cooled-on pattern. See KB.DomainDrivenNotPlatform for how to apply it.
source: value-pivot session 2026-07-02
Domain-driven, not platform - the engine is plumbingThe operating discipline: build for ONE real domain/job/user, measure a real-world outcome not green tests, and treat the agent harness as plumbing not the product. The validate-first playbook.
LESSON (value-pivot session, 2026-07-02): the exit from KB.WhyAgentProjectsStall is a discipline, now written into the IOODARI value litmus. Build for ONE real domain, ONE real job, ONE defined user - not a general platform. Measure a REAL-WORLD OUTCOME (a euro saved, hours returned, a document a buyer accepts), never green tests or part-counts. The engine/harness (SPICE's agent runtime, the crew pattern) is PLUMBING, not the product - point it at a real job; do not polish the plumbing as if it were the deliverable. The validate-first playbook, in order: (1) list the narrow candidate jobs for a real user; (2) talk to about 10 real buyers about the PROBLEM before writing code; (3) do the job BY HAND once (a concierge run on real data) to prove the value in euros and hours; (4) only then automate the PROVEN workflow. If a loop's honest output is "more machinery" with no outside-world deliverable, STOP and repoint. This is the through-line the operator forced across the 2026-07-02 session ("agents, agencies, crews has to be valuable, otherwise better to stop").
source: value-pivot session 2026-07-02
AG-UI is the agent-to-UI leg - borrow the event vocabulary, not the Microsoft SDKWhy V25.40 adopted the open AG-UI protocol (the third agent-native leg beside MCP and A2A) as a native XSLT-projected SSE seam rather than taking the Microsoft Agent Framework AG-UI SDK.
DECISION (operator "do whats best for the future" + "is there an sdk for ag ui", 2026-05-31): AG-UI (the Agent-User Interaction Protocol) is the open, event-based standard for the agent-to-FRONTEND channel - the third leg of the agent-native trinity beside MCP (agent-to-tools, SPICE has it) and A2A (agent-to-agent, see KB.A2AAgentCardDiscovery). It is a stream of ~16 typed JSON events over SSE/WebSocket: lifecycle (RUN_STARTED/FINISHED/ERROR), text (TEXT_MESSAGE_*), tool calls (TOOL_CALL_START/ARGS/RESULT/END), and state (STATE_SNAPSHOT/DELTA). A .NET SDK exists (Microsoft.Agents.AI.AGUI client + Microsoft.Agents.AI.Hosting.AGUI.AspNetCore server, Nov 2025) as part of Microsoft Agent Framework. WE DID NOT TAKE THE SERVER SDK: it is built around the framework's AIAgent + Microsoft.Extensions.AI model (SPICE's agent is IAgentMessageBus + competency routing + the pipeline harness, NOT an AIAgent), it drags the whole Agent Framework dependency tree into a codebase whose ethos is one engine dep (SaxonCS) + no-dep everything else, and it bypasses SPICE's actual moat (the XML spine + XSLT projection). The one bit of real value - correct event framing - is a small typed-event set available from the spec for free. SO: borrow the standard's event VOCABULARY, emit it the SPICE way - each event is an XML envelope (AgUiEventStream), projected to spec-shaped AG-UI JSON by AgUiEvent.xslt over Saxon (XSLT 3.0 method=json) - the "convert to other forms via an XSLT adaptor, never hand-rolled JSON" rule (feedback_xml_self_describing_xslt_adaptor) applied at the agent-to-UI edge, where the browser is genuinely the outside so JSON-at-the-edge is correct. Same pattern as CloudEvents/Schematron/OpenXML. REUSE-FIRST: STATE_SNAPSHOT = the existing board model (builder or spine-query); TOOL_CALL_* = a board action's Tool via IToolRegistry (the U2 dispatch, the same path MCP uses). Served: GET /agui/{board}, POST /agui/{board}/{op}. The Microsoft CLIENT SDK stays a later option IF/WHEN SPICE needs to CONSUME an external AG-UI agent (embed a partner agent in the Ship console) - that direction does not touch the spine. OWED follow-ons: deep-JSON snapshot (xml-to-json the model, not the XML string), long-lived/resumable SSE with STATE_DELTA + TEXT_MESSAGE_* (wire the Ship Assistant + live observability layer as the consumer - the payoff), and a spec-conformance check against the AG-UI test suite. Sources: docs.ag-ui.com/introduction ; github.com/ag-ui-protocol/ag-ui ; learn.microsoft.com/en-us/agent-framework/integrations/ag-ui ; copilotkit.ai/blog/master-the-17-ag-ui-event-types.
source: SPICE.Web/Controllers/AgUiController.cs
SPICE prices per Sealed-division outcome atop a flat platform baseWorld intel: hybrid pricing (flat base + per-outcome) is the 2026 AI-SaaS norm proven at $100M-$400M ARR; SPICE's billable unit = one agent-built division reaching Sealed status, metered off the ArtifactPackage Sealed event.
WORLD INTEL (probe scout, 2026-05-31): outcome/hybrid pricing is the settled 2026 AI-SaaS model and is proven at scale - Intercom Fin reached $100M+ ARR charging $0.99 per RESOLVED support ticket (only billing on delivered outcome) with a $1M performance guarantee; Lovable hit $400M ARR on tiered subscription + per-generation credits. 43% of SaaS already uses hybrid (flat base + variable consumption), projected 61% by year-end; AI-first margins run 50-60% (vs 80-90% traditional) because of compute, so usage top-ups pass inference cost to the buyer and protect margin as model costs change. FOR SPICE: define the model as a flat monthly platform fee (control plane + routing + gallery access) PLUS one billable unit per agent-built division that reaches Sealed status - SPICE's ApprovalGate + gated-maker + Sealed-status flow already defines a clean value-event, and the ArtifactPackage manifest already records identity + lineage (the metering hook). Offer a 30-day satisfaction guarantee on the first deployment to kill adoption friction. AppSource transactable offers take only a 3% fee, handle invoice/tax, and let enterprise buyers spend MACC commitment - register SPICE as a transactable SaaS offer with metered pricing. Sources: thegtmnewsletter (Intercom Fin) ; sacra.com/c/lovable ; saasmag.com/how-saas-companies-monetizing-ai-agents ; dupple.com/learn/what-is-microsoft-appsource.
source: world-probes intel 2026-05-31
The SPICE moat is XSD-validated, provenance-sealed artifacts - not 'an agent that builds SharePoint'World intel: Microsoft now auto-provisions a Copilot agent per SharePoint site (March 2026) + ships a Power Apps MCP Server (GA May 2026), commoditizing the build-SharePoint loop; SPICE's defensible delta is the XML-native, Schematron-guardrailed, agency-provenance-sealed typed Envelope / ArtifactPackage.
WORLD INTEL (probe scout, 2026-05-31) - the THREAT CLOCK is loud. Microsoft confirmed (Ignite 2025 / March 2026) that every SharePoint site now auto-provisions a ready-made Copilot agent scoped to site content, with natural-language site/list/library creation and Copilot Studio binding agents to SharePoint lists - this natively replicates SPICE's crew-provisioning + Mission Division loop, licensed per M365 Copilot seat. Microsoft also shipped the Power Apps MCP Server to GA (May 4 2026), routing agents to SharePoint folders/mailboxes as unstructured sources. And Lovable ($400M ARR, Feb 2026, 200k projects/day, 50% enterprise) proves huge demand for natural-language app generation. CONCLUSION: 'an agent that builds SharePoint things' is no longer defensible. SPICE's moat must harden onto what Microsoft cannot commoditize and Lovable cannot provide: XML-native, XSD/Schematron-validated, agency-provenance-SEALED ArtifactPackages + the typed Envelope contract - auditable schema-gated quality, provenance lineage, and cross-site composability. ACTIONS: write this strategic delta into plans/Vision.md explicitly; add a /gallery TRUST-TIER filter surfacing the ArtifactPackage manifest quality (Sealed vs Draft, schema-valid vs not) as a visible enterprise buy signal; position templates as 'SharePoint-on-prem-fidelity, auditable, XSD-validated, provenance-sealed artifacts' not 'app starters'. Sources: sharepointlibrary.com/sharepoint-roadmap-2026 ; microsoft.com power-apps MCP public-preview ; sacra.com/c/lovable.
source: world-probes intel 2026-05-31
Control-plane provider resilience + data-residency gate for DeepSeek-class providersWorld intel: DeepSeek's ~97.8% uptime and train-on-API-data ToS require a circuit-breaker fallback route + a router-level data-residency gate that blocks customer-identifiable payloads from any train-on-data provider - mandatory before /gallery customer data flows.
WORLD INTEL (probe scout, 2026-05-31): two infrastructure risks on the exact surface SPICE wants to monetize. (1) AVAILABILITY - DeepSeek had multiple 7h+ outages in 2025-2026 (worst 7h13m, March 30 2026) and ~97.8% trailing 12-month uptime (~8 days/year down). SPICE's Genesis crew + production runs route through DeepSeek-V3, so an outage stalls EVERY gated maker run. (2) CONFIDENTIALITY - DeepSeek's ToS permits training on API-submitted data BY DEFAULT, unlike Anthropic/OpenAI/Google which exclude API data; a marketplace customer's identifiable data could leak into a competitor-trained model. ACTIONS (before any /gallery customer data flows): add a circuit-breaker FALLBACK route in the control plane (echo/stub for CI; a configurable secondary such as Anthropic Haiku for production) tripping on 5xx or timeout; add a router-level DATA-RESIDENCY competency gate that blocks customer-identifiable payloads from any train-on-data provider. This is a correctness + compliance + availability blast radius, not a nice-to-have. Mirrors KB.PaperclipWisdom's bounded-autonomy reflex: gate the risky path, keep the human/governance in the loop. Source: techrepublic.com (DeepSeek 12-hour outage) + DeepSeek ToS.
source: world-probes intel 2026-05-31
SaxonCS-HE 13.0 license, pinning, and graceful-degrade contractWorld intel: SaxonCS-HE 13.0 is the first free HE tier for .NET (MPL 2.0, brand-new, revocable); pin the NuGet version, recompile Saxon-12 SEFs, add an IAdvancedXmlEngine null-fallback so a future term/build change degrades the XSLT 3.0 path instead of crashing boot; adopt fn:element-to-map + multi-schema validation.
WORLD INTEL (probe scout, 2026-05-31): SPICE adopted SaxonCS-HE 13.0 (V25.12) the DAY it released (2026-05-29) - the first-ever free Home Edition tier for .NET 8+ (Saxon 12 for .NET was commercial-only EE). The free HE tier is brand-new, unproven at scale, MPL-2.0 licensed (source available, redistribution not straightforward), and REVOCABLE - Saxonica made the 12.x era commercial-only before, so a future 13.x minor could change terms or break the HE build. RISK ACTIONS: pin the exact Saxon NuGet version in the csproj (no auto-upgrade into a future 13.x); add an IAdvancedXmlEngine null-fallback / feature-flag so a broken HE build DEGRADES the XSLT 3.0 path gracefully instead of failing boot (the SpineQueryService already treats the engine as nullable - extend that reflex to rendering); recompile any cached SEF files (Saxon-12 SEFs are incompatible). CAPTURE the new 13.0 capabilities: fn:element-to-map (XML to JSON) is the XSLT 3.0 replacement for the hand-rolled JSON path flagged in feedback_xml_self_describing_xslt_adaptor; simultaneous multi-schema validation lets each Typed Envelope E2 per-task-type payload XSD validate independently; expression elaboration gives ~20% perf. .NET note: .NET 10 is LTS to Nov 2028; SaxonCS 13.0 targets .NET 8+ (compatible); do NOT target .NET 11 (STS preview) - next gate is .NET 12 LTS (2027). Source: saxonica.com/html/products/latest.html (MPL 2.0).
source: world-probes intel 2026-05-31
Replaceable-agents staffing - the deputy mechanism and the low-criticality accept decisionWhy the replaceable-agents/staffing theme is closed: the deputy engine auto-furnishes redundancy for every mission-critical (Lead+) seat, and the residual single-covered seats are intentionally accepted because redundancy is not free for low-criticality work (the FMECA lens). The invariant a future cycle must preserve.
DECISION (operator "do a and b", verified live 2026-05-29, V24.30): the ★ replaceable-agents / staffing theme is CLOSED. The principle: living procedures/tools/skill-SEATS belong to the work environment (department/site/phase) and are injected; an agent is a swappable, competency-matched part slotted into a seat - swap the agent, the work stays. MECHANISM (already shipped, no new engine): (1) the /coverage board (V22.4, SeatCoverageBuilder) matches each department's declared seats (SiteBlueprint Skills/PartRef = skill@role) against placed operators, gated by EvaluateSkillCompetency + role authority, labelling each seat Uncovered (0 candidates) / Covered (1 = single point of failure) / Replaceable (>=2). (2) the DEPUTY GENERATOR (V22.18, ActorResolver.GenerateDeputyOperators) auto-synthesises Actor.{Dept}Deputy for every department whose maximum seat role >= DeputyMinRole (default Lead, set in Scope.GeneratedOperator), converting single-covered mission-critical seats to Replaceable with zero authoring. VERIFIED LIVE (/coverage/data, V24.30): single=3, uncovered=0, replaceable=101. Engineering/Skill.ApproveRelease@Manager is Replaceable (Candidates=2) via the auto Actor.EngineeringDeputy (PartRole Manager=3 >= Lead=2, so the generator fires) - the old V22.17 authority gap is closed. THE FMECA DECISION (the heart of this article): the 3 residual single-covered seats - Skill.AssistDeveloper, Skill.WriteDocumentation, Skill.ResearchTechnical - are ALL Assistant/Member level, BELOW DeputyMinRole=Lead, so the generator deliberately does NOT auto-deputise them. This is ACCEPTED, not a gap: redundancy is not free, and a comfort seat (low severity) does not earn a backup. This mirrors KB.PaperclipWisdom's bounded-autonomy reflex - spend resilience only where criticality warrants it. THE INVARIANT a future cycle must preserve: no seat bound at Lead or above is single-covered (uncovered=0 AND every Status=Covered seat has BoundRole < Lead). If a new Lead+ seat appears uncovered/single, either place a competent operator or rely on the generated deputy - do NOT lower DeputyMinRole without intent (it would mint deputies for comfort seats and dilute the signal). To deputise a specific low-criticality seat anyway, add an explicit ActorProfile (the Actor.OperationsDeputy pattern) - but prefer the auto path (drift-free). Do NOT add an IsDeputy/Criticality field to the ActorView or SkillBinding records casually: both are positional with many callers (a field shift is a mass compile break); a per-seat Criticality attribute is the deferred fuller-FMECA model and needs XSD + DepartmentRoster changes (ask first).
source: memory/project_replaceable_agents_gap.md
n8n wisdom borrowed into the SPICE beltWhat the SPICE production belt borrows from n8n (the node-based workflow tool), and the decision to XSD-validate ONLY at untrusted-source boundaries. The standards-borrowing reflex (DRY) applied to the pipes-and-filters belt.
FRAMING: n8n is a mature node-based workflow engine; its hard-won patterns map cleanly onto SPICE's content-manufacturing belt (production lines = pipes-and-filters; building-block machines = nodes). Borrow the standard rather than reinvent (feedback_investigate_online_before_inventing). FIVE n8n patterns and their SPICE form: (1) TYPED ENVELOPE - n8n passes a standard data envelope node-to-node (an items array of {json, binary}); each node reads only its slice and passes the rest on. SPICE belt was a flat string-bag; V24.3 adds a typed XML envelope (ProductionLineExecutor.PayloadKey = the {{payload}} token: <PipelinePayload> with <Input> rows + a <Slice station/machine/trust> per step). DONE (additive; the flat keys + priorOutput are unchanged). (2) EXPRESSIONS / reference-an-upstream-node-by-name - n8n's {{ $node[\"Name\"].json.x }}. SPICE form: a station declares {{payload}} and Tool.SelectXPath's any prior slice BY NAME via XPath (e.g. //Slice[@station='Research']) - not just the immediately-preceding priorOutput. DONE (V24.3, via the envelope + the existing V23.15 Tool.SelectXPath). (3) VALIDATE AT THE BOUNDARY - n8n validates item structure at nodes. SPICE STEER (operator, 2026-05-28): XSD-validate ONLY UNTRUSTED sources, never trusted internal hops (matches 'validate at system boundaries; trust internal code'). SPICE form: Scope.MachineClassification:Untrusted declares which machines cross a trust boundary (external services + the model: Llm/WebSearch/DeepResearch/ArXiv/FetchKnowledge/FindStockMedia/GenerateImage/GenerateVideo); a step may declare an OutputSchema; the executor validates an untrusted step's output against it at the boundary and fails loud on reject; trusted deterministic transforms (MakePage/TransformXslt/SelectXPath) flow unchecked. DONE (V24.3). (4) PER-NODE RETRY / CONTINUE-ON-FAIL / error-workflow - n8n nodes can retry or continue past failure into an error path. SPICE today fail-fast + Tool.ApprovalGate. TODO: a station-level retry/continue-on-fail policy (declared). (5) BRANCH / MERGE / IF / SWITCH + TRIGGER taxonomy (webhook/cron/poll/manual) - n8n's non-linear routing + trigger nodes. SPICE today: linear pipelines + DecisionTable/DMN for decisions; cron standing orders + webhooks for triggers. TODO: conditional multi-path routings + a unified trigger part. PRINCIPLE: keep the DNA - classification + schema + envelope are all declared data/XML, the executor is the only engine seam.
source: plans/ContentManufacturing.md
The City Registry - the Yellow Pages for agentic partsThe city's central catalogue where any agency/builder agent discovers parts (purpose, required+optional inputs, output, access level) - and the key insight that SPICE ALREADY IS this registry (the part spine, served three ways, RBAC built in). Operator vision 2026-06-07.
FRAMING (operator vision 2026-06-07 - "a clear telephone book / central web service that agencies use to see the parts like a factory: what input is required and optional, what output to choose, the general purpose/mission and services it provides; a skill or MCP for builder agents, scoped by role"): KEY INSIGHT - SPICE ALREADY IS this registry. The part spine IS the catalogue, exposed THREE ways: (1) HTTP - /agency/parts, /agency/parts/by-id/{id}, /agency/parts/{kind}, /agency/tools (each Tool's ToolDescriptor = Name + Description + ArgsJsonSchema = its purpose + required/optional inputs), /agency/skills/for-role/{role}, /agency/types (the C# structural map). (2) MCP - /mcp/jsonrpc (McpController + McpRegistry) exposes Tools + SavedQueries + Workflows as MCP tools, and EVERY part as an MCP resource (spice://parts/{Id} with Kind+Name+Classification+MinimumRole), so an external builder agent discovers and calls the SAME catalogue (channel symmetry). (3) XQUERY - Tool.XQuery + tools/parts-xq.ps1 query the merged spine live. RBAC IS BUILT IN: every Part carries MinimumRole (Assistant.Member.Lead.Manager) + Classification (Public.Internal.Confidential.Restricted.Critical); IPartLibrary.AvailableTo(role, maxClassification) scopes visibility; KnowledgeArticle adds Compartment (SCI need-to-know). The operator's Creator-vs-Operator access levels MAP to PartRole: a builder/harness agent sees+drafts at Lead/Manager; an execution crew discovers+calls stable production parts at Member. GAP (the next arc, reuse-first - NOT a new service): Tool PARTS do not yet declare an OutputSchema, a Taxonomy path, or a Compartment (Skills/ContentTypes are richer); /agency/tools is not yet role-gated; there is no tool-to-skill capability link. The upgrade is to ENRICH the manifest on existing Tool parts + add ONE registry-lookup skill/MCP face over what already serves - not build a parallel registry. See KB.OtbPartsNotCode (the OTB rule) + KB.DualStageVerification (proving a part works).
source: plans/Vision.md
OTB parts not code - a new capability is a new PART, plus the no-orphan-code axiom and RBAC encapsulationThe governance rule that the city's out-of-the-box components ARE the XML parts (Tool/Pipeline/Skill/Workflow), never new C# unless a reusable core Tool/engine seam (the n8n-node analogue); agents emit schema-valid parts, never loose code; Creator vs Operator access. Operator vision 2026-06-07, translated to SPICE DNA.
FRAMING (operator vision 2026-06-07 - the pasted spec said "Python OTB / Pydantic", which is generic; SPICE's out-of-the-box components ARE the XML parts, so translate the rule to the DNA, do NOT adopt Python): THE OTB DEFAULT RULE - a new capability is a new PART (a Tool, a Pipeline/Routing, a Skill, a Workflow, a DecisionTable, a ContentType - declared in XML), NEVER new C#, EXCEPT a genuinely-new reusable CORE component: a new Tool invoker or an engine seam (interface / registry / strategy / executor) - that is the n8n-NODE analogue (the reusable block other parts plug into). C# is never business logic; rules + templates + content + payloads live in XML/XSD/XSLT/XQuery (see reference_developer_rules + KB.SpicePrinciples). THE NO-ORPHAN-CODE AXIOM: an agent must not leave a standalone code block in a text reply; every functional output is encapsulated in a schema-valid PART (validates against SAF-Parts.xsd, carries its role + classification + taxonomy) so it is discoverable in the registry (KB.CityRegistry), governed, and reusable - not lost in a transcript. ROLE-BASED ENCAPSULATION (maps to PartRole): CREATOR level (builder/harness agents, Lead/Manager) view + draft + modify part definitions across the technical nodes; OPERATOR level (execution crews, Member) discover + invoke STABLE production parts only, and never see or edit the source prompt/structure. WHY THIS MATTERS: it is exactly what keeps the factory modular and self-sustaining - parts plug together like n8n nodes (KB.N8nWisdom) but stay governed by the spine, so the city can grow by adding data, not code.
source: plans/Vision.md
Dual-stage verification - agentic audit first, then the automated harness; plus the parallel-compare-migrate loopHow a part/pipeline is proven to really work: Stage 1 agentic semantic audit (cheap, before execution), Stage 2 automated harness (free, deterministic) - mapped onto the SPICE parts that already do each - and the build-parallel, compare, switch-if-proven upgrade loop. Operator vision 2026-06-07.
FRAMING (operator vision 2026-06-07 - "where is verification that parts really work + functional and technical requirements are met; FIRST agentic verification then more automated free harness checks"): a part/pipeline is proven by a DUAL-STAGE quality gate, and SPICE ALREADY HAS BOTH stages - reuse them. STAGE 1 - AGENTIC SEMANTIC AUDIT (first, cheap, before execution): a crew/tool reviews the draft for logic, type + taxonomy mismatch, prompt-injection and infinite-loop vectors. SPICE form: Tool.Advise (gathers the city's context, proposes, never acts - the anti-paperclip suggester), the Schematron guardrail engine (SchematronValidator - business rules the XSD cannot, emits SVRL), and Tool.DualDispatch A/B (two same-domain agencies cross-check; the eval loop scores the outcome). STAGE 2 - AUTOMATED HARNESS (free, deterministic, after audit): boot the part in isolation, inject mock inputs, assert outputs + metrics. SPICE form: Tool.QualityGate (mustContain / forbid / mustMatch-XPath acceptance criteria; fail-fast HALTS the belt), XSD boundary validation (an untrusted step's output validated against its declared OutputSchema/ContentType), the unit-test suite (2871+), and tools/obs-smoke.ps1 (boot + assert the SERVED surface, not just the model - anti-patterns #23/#26). FUNCTIONAL vs TECHNICAL requirements: functional = the agentic audit + the QualityGate acceptance criteria (does it do the job?); technical = XSD/Schematron/type-boundary + telemetry (cost + timing on the run genealogy). THE PARALLEL-COMPARE-MIGRATE LOOP (operator: "more research says better way of work - upgrade parallel, compare, test, switch, migrate to new if proven loop"): when research finds a better way, build the new part ALONGSIDE the proven one, run BOTH on the same input (Tool.DualDispatch IS this A/B harness), compare via the eval loop, and switch + retire the old ONLY when the new measurably wins - never a blind replace. OWED (designed, not wired): the Schematron-to-briefing retry-with-hint loop (E3b) and a self-critique gate before sealing a single artifact.
source: plans/Vision.md
Business rules engine - we use DMN plus Schematron, not BizTalk RDL/ReteAnswer to: do we have a BizTalk-style XML Business Rules Engine / Rule Definition Language (RDL)? SPICE has XML-declared business rules but borrowed the modern open standards (OMG DMN + ISO Schematron) over BizTalk's proprietary RDL+Rete; the one gap is a true forward-chaining inference engine, which is not needed. Operator question 2026-06-07.
FRAMING (operator question 2026-06-07 - do we have a BizTalk-style XML Business Rules Engine / Rule Definition Language RDL?): SPICE HAS XML-declared business rules, but borrowed the MODERN OPEN STANDARDS (OMG DMN plus ISO Schematron) instead of BizTalk's proprietary RDL-plus-Rete - the borrow-the-standard DNA rule (BizTalk is legacy/deprecated; the industry moved to DMN exactly here). MAPPING BizTalk BRE to SPICE: (1) Policy/ruleset to DecisionTable parts (DMN decision logic; e.g. DT.AgencyTriage via RequestToolInvoker - condition columns to an outcome). (2) Rule conditions/predicates over facts to Schematron assertions (SchematronValidator - XPath conditions emit SVRL; the business rules the XSD cannot express). (3) Forward-chaining when-X-do-Y to CrossModuleRule (CrossModuleRuleDispatcher) plus EventReceiver to Workflow to Phase (event-driven firing). (4) Rule actions/enforcement to Tool.QualityGate (mustContain/forbid/mustMatch-XPath, fail-fast HALTS the belt) plus IPolicyEngine (the write-gate). (5) Facts (XML docs/objects) to the typed envelope plus the part spine, queried by Tool.XQuery/Saxon. EQUAL-OR-BETTER for decision logic (DMN) and fact-validation (Schematron): open standard, self-describing XML, native Saxon engine, no BizTalk runtime. THE ONE GAP: no true Rete forward-chaining INFERENCE engine (assert facts into a working memory and let rules cascade iteratively until quiescence - the Drools/BRE model). DMN is single-pass decision logic; CrossModuleRule plus EventReceiver gives a COARSE forward chain (an action changes an item which fires another receiver), not a Rete loop. VERDICT: do NOT adopt BizTalk RDL/Rete - DMN plus Schematron is the 2026 consensus and covers the need; if a real multi-pass inference need ever appears, add a Rete engine behind an interface seam (a new reusable engine component per KB.OtbPartsNotCode), never a BizTalk dependency. See KB.DualStageVerification (the QualityGate/Schematron quality gates) plus reference_developer_rules.
source: plans/AgencyArchitecture.md
SharePoint-on-prem web part placement borrowed into the Parts FactoryHow Tool.MakeWebPart (the placing machine) reuses SP-on-prem web part wisdom: SPLimitedWebPartManager.AddWebPart + the Web Part Gallery (_catalogs/wp) + web part zones - all as XML DNA, no JSON. The standards-borrowing reflex applied to placing web parts onto pages.
FRAMING (operator steer, 2026-05-28: "reuse wisdom from sharepoint onprem" + "no json use our dna"): SharePoint-on-prem has a mature, battle-tested model for putting web parts on pages; borrow it rather than invent (feedback_investigate_online_before_inventing, the DRY reflex). THREE SP-on-prem concepts and their SPICE form: (1) THE WEB PART GALLERY - SP keeps reusable web part definitions in the site-collection gallery (_catalogs/wp) as .webpart / .dwp XML files you pick from when adding a part. SPICE form: Scope.WebPartTemplates - a SettingScope whose each Setting Value is one <WebPart Type='...' .../> fragment with {{tokens}} (DATA, not code). It is the single-web-part sibling of Scope.PageTemplates (the page-LAYOUT gallery). The gallery being CURATED is itself the validation that the @Type resolves - only known web part types live there (so the Foundation tool needs no reference to the Web-layer IWebPartRegistry). (2) SPLimitedWebPartManager.AddWebPart(webPart, zoneId, zoneIndex) - the SP API that adds a web part instance into a named zone of a page at an index. SPICE form: Tool.MakeWebPart (the PLACING machine, V24.13) - clone a gallery entry BY NAME, fill its {{tokens}} (residual-token guard: an unfilled token would land in an attribute, so fail loud), read the existing ContentType.Page's Layout via the storage seam, append the <WebPart> into the named <Zone> (created if absent), and re-write the item via the same provisioning seam Tool.MakePage uses. Idempotent on Type+Title (re-running does not duplicate). (3) WEB PART ZONES - SP pages declare zones; web parts live inside them. SPICE form: the existing <Layout><Zone Name='...'><WebPart/>...</Zone></Layout> grammar (Field.Layout), reused unchanged. DNA DISCIPLINE (operator: "no json use our dna"): the gallery, the layout, and the provisioning op are all XML; the only JSON is the engine's pre-existing internal storage serialization (AddListItemOpHandler), read untouched - no new JSON is introduced in the authoring surface. PAIRING: this closes the task #30 pair - the reusable Projection web part (re-home a board as a web part) shipped in V23; the placing machine that puts it onto a factory page shipped now. LIMITATION-AS-RECEIPT: edits storage-backed (factory-made) pages only - a manifest-declared page shadows storage at resolution time, so Tool.MakeWebPart fails clearly for one (manifest pages are authored in the manifest). The reader-resolves assertion (anti-pattern #25) is met by the endpoint smoke (#26): GET the page and confirm the placed Projection board renders its real fragment, not a diagnostic.
source: plans/PartsFactory.md
The money meter, the credits gate, and the proactive improvement deckIn-app documentation for the Commerce.GalleryCredits wedge (prepaid credits = granted + topped-up - burned, viewable at /board/credits with token totals), the OPT-IN consumption gate that refuses new LLM work when the balance is exhausted, and the weekly division that proactively researches system improvements and delivers a presentation deck to the Hub inbox.
The money half + the proactive-improvement half, as shipped. All of it is DATA + reused seams; no bespoke billing engine. ================================================================ 1 - THE CREDITS METER (/board/credits) ================================================================ Credits are the SaaS unit over the city's heterogeneous LLM spend. 1000 credits = $1. remaining = granted + topped-up - burned - GRANTED: the baseline free grant, DATA at Scope.Commerce / Commerce.Credits.Granted (default 1,000,000 ~ $1000). Re-grant with no recompile. - TOPPED-UP: each top-up is a Paid Invoice (ContentType.Invoice, SupplierSku=SPICE.Credits) summed onto the balance. Top up at /board/credits (the "Buy credits" form) or POST /gallery/topup amount=N buyer=X - $1 buys 1000 credits. - BURNED: the real router cost ledger (IRouterCostStore), the same ledger /board/efficiency uses. - TOKENS: the meter now also shows input/output token totals per call. They read 0 when the active provider does not report usage (the live DeepSeek path records cost-by-chars); they light up for a provider that returns token counts. One source of truth (ICreditsBalance) feeds both the meter and the gate, so they can never disagree. ================================================================ 2 - THE CONSUMPTION GATE (opt-in, OFF by default) ================================================================ A prepaid balance only means something if it can run out. The gate (CreditsBalanceBeforeInvokeHook, a sibling of the cost-budget hook) cancels a router LLM call when the balance is exhausted. - It is INERT unless Commerce.Credits.Enforce is set true at Scope.Commerce. Shipping it changed nothing until you arm it. - When armed AND remaining <= 0, the call is cancelled (a synthetic "credits exhausted" result + an audit row + the cost-gate metric). Top up to lift the gate. - It is FARM-wide (one prepaid pool for the whole city). NOTE: arming it while exhausted blocks every agency - per-account partition + a self-exclusion for the city's own survival loops are the next slice. ================================================================ 3 - THE PROACTIVE IMPROVEMENT DECK (weekly, RT-IMPROVEMENT-DECK) ================================================================ The Research and Enrichment division now produces a PRESENTATION about system improvements, on its own, every Monday 07:00: Tool.DeepResearch (research SPICE self-improvement opportunities, emit a Markdown slide outline) -> Tool.RenderSlideDeck (Studio: outline -> a self-contained HTML5 deck) -> Tool.SendMail (deliver the deck to the Hub inbox, attributed to the division) Read the delivered decks at /sites/Hub/Lists/Inbox. The run seals cost-bound on /board/outcome and its spend shows on the credits meter above. Pure data (a standing-order Setting), reusing the existing Studio deck tools - no new code.
source: cycles Commerce.GalleryCredits + Commerce.GalleryCredits.Slice2 + RT-IMPROVEMENT-DECK
SPICE - the state of the system (three insights + UMLs)An honest, one-page map of what SPICE actually is today: the self-building city, the XML-DNA way it is built, and the candid live-vs-parked ledger - with four generated UML diagrams (architecture, city topology, the self-build loop, the live agency cadence). The orientation page for when the breadth feels overwhelming.
SPICE in one breath: a SharePoint-modelled, self-building city of AI agencies. It runs on 45 SharePoint sites, 7 living agencies plus an 8th it gave birth to itself, and a closed loop that lets it sense its own gaps and propose new divisions for you to approve. Everything below is the territory, not the brochure - including what is dormant. ================================================================ INSIGHT 1 - SPICE is a self-building organism, and the loop is CLOSED. ================================================================ The headline capability is not "agents that chat" - it is a city that grows itself. Weekly, the city ranks its most-demanded unmet capability, drafts a minimal new agency, and PARKS one approval request. You approve; a quorum flips it; the applier births the district, gives it a recurring standing order, and its first output lands in your Hub inbox. No step is faked - it has run end to end (the born agency "competitormonitorpricingweekly" is live and graded). The human stays the decision-maker by design: the factory SUGGESTS, it never SEIZES.
sequenceDiagram
  participant City as City (DemandRanker)
  participant Auto as Tool.AutoGenesis
  participant Gate as ApprovalRequest
  participant Op as Operator (you)
  participant Q as Quorum + Applier
  participant Ag as Born agency
  participant Inbox as Hub inbox
  City->>Auto: sense most-demanded unmet gap (weekly)
  Auto->>Gate: PARK one Open request (always gated)
  Op->>Gate: Approve verdict
  Gate->>Q: quorum reached - flip and apply
  Q->>Ag: birth district + RT-AGENCY standing order
  Ag->>Ag: recurring work (Tool.DualDispatch)
  Ag->>Inbox: deliver output (Tool.SendMail)
  Inbox->>Op: you read the result
================================================================ INSIGHT 2 - It is all XML DNA, rendered the SharePoint way. C# is only the engine. ================================================================ There is no business data in code. Prompts, protocols, cron, thresholds, sites, agencies, skills, decision tables - all live as schema-validated XML parts across Parts.xml plus 13 app manifests, validated by SAF-Parts.xsd, projected to every screen through XSLT 3.0 (this very page is a KnowledgeArticle rendered by Knowledge.xslt). C# holds only idempotent engine seams: the loader, the registry, the strategy executors. That is the moat - the platform is declare-first, so adding a capability is usually adding XML, not writing code.
flowchart TD
  subgraph DNA["XML DNA - declare-first"]
    P["Parts.xml + 13 app Parts.*.xml"]
    X["SAF-Parts.xsd / SAF-Caml.xsd"]
    M["SAF-Site-Manifest.xml (45 sites)"]
  end
  X -. validates .-> P
  P --> L["PartLibrary - loader + registry"]
  M --> SPV["Site provisioning"]
  L --> ENG["Engine seams - C#, idempotent only"]
  ENG --> XS["XSLT 3.0 transforms"]
  XS --> UI["Served surfaces: /sites, /board, /observatory"]
  SPV --> UI
  subgraph APPS["Composable layers"]
    OBS["Apps.Observatory"]
    MKT["Apps.Marketplace"]
    PLG["Plugins.Saxon / DeepSeek"]
  end
  APPS --> L
The 45 sites are not flat - they cluster into a city with a command hub at the centre.
flowchart LR
  HUB["Hub - operator command center"]
  SVC["Services - taxonomies + ActorMemory"]
  HUB --- SVC
  subgraph SELF["Self-building divisions"]
    GEN["Genesis"]
    REC["Reception"]
    VEN["VentureEngine"]
    LIFE["LifeAssistant"]
  end
  subgraph ERP["ERP modules V12-V14"]
    CMN["Common - master data"]
    DIST["Distribution"]
    FIN["Finance"]
    INV["Inventory"]
    MFG["Manufacturing"]
    CRM["CRM"]
  end
  subgraph GOV["Governance"]
    APPR["Approvals - quorum"]
    COMP["Compliance"]
    LEG["Legal"]
    ENGG["Engineering - dogfood"]
  end
  subgraph EXT["External portals - OIDC-scoped"]
    CLI["Client"]
    VND["Vendor"]
    EMP["Employee"]
  end
  HUB --> SELF
  HUB --> ERP
  HUB --> GOV
  HUB --> EXT
================================================================ INSIGHT 3 - The honest ledger: what is ALIVE, and what is PARKED. ================================================================ ALIVE - 7 hand-built agencies, all with real recurring work graded honestly on /board/health, plus 1 the city birthed itself, plus 2 gated weekly automations (seal-a-skill and birth-an-agency).
flowchart TD
  subgraph LIVE["Living agencies - real cadence"]
    W["Wisdom - daily 07:00"]
    R["Research - daily 07:00 (A/B)"]
    AC["Academy - daily 09:00"]
    C["Content - daily 10:00"]
    G["Genesis - daily 11:00"]
    S["Strategy - Mon 08:00"]
    PF["PartsFactory - every 10-20 min"]
    BORN["competitormonitorpricingweekly - born"]
  end
  subgraph GATED["Gated weekly automations"]
    EX["RT-EXTRACT - seal a proven skill (Mon 09:00)"]
    AG["RT-AUTO-GENESIS - birth an agency (Mon 10:00)"]
  end
  LIVE --> BH["/board/health - honest grading"]
  GATED --> BH
PARKED / LOST - the candid part. Roughly 17 features are intentionally parked with documented reasons (Commerce.GalleryCredits, A2UI.FormsInterop, Approvals.OneClickExtranet, the SimCity skin, the Observatory QuestBoard, SP-Field vocabulary, AddActorSkill, and more). Around 9 things are possibly orphaned rather than chosen: 7 SPICE.Apps.Erp.* project stubs that were never routed to the agency layer, the Board.Atlas builder gap, and the older OodaLoop app superseded by today's eval loop. 3 are cleanly superseded (the LiteLLM proxy, replaced by direct Baseten routing). THE ONE HONEST GAP worth saying out loud: the money-half keeps slipping. The last several arcs were all internal (memory, self-build, the Warfront RTS). Commerce.GalleryCredits shipped only as a read-only probe. If you feel "we built a lot but where is the revenue" - that instinct is correct, and it is the next feature arc, not a blind spot. Where to look next: /board/health (live grading), /board/schedule (standing orders firing), /board/agencies (the roster), /observatory (the 3D station), /sites/Hub (your command center + inbox). This page lives at /board/knowledge.
source: plans/AgencyArchitecture.md + SAF-Site-Manifest.xml census
SPICE Architecture PrinciplesCore design principles every contributor should know
SPICE follows a strict layered architecture: SPICE.Core (domain, interfaces, options) has no external dependencies; SPICE.Infrastructure implements Core's interfaces; SPICE.Web composes the application. Dependencies flow inward only. Use constructor-injected DI exclusively - no static service locators. Configure with the Options pattern, not magic strings. Storage is pluggable via IStorageProvider. XSLT/XML/XSD is the declarative DNA - prefer schema-validated XML configs over JSON for the platform contract. Singletons must NOT inject scoped services directly; use IServiceScopeFactory.
source: plans/ARCHITECTURE.md
Start here - how to use this shipWhat this manual is, who it is for, and the chapter map.
This is the Ship's Manual: the curated "how do I use SPICE" guide. It serves three audiences from ONE source - the human operator (read this page at /board/manual), the in-app agents (query these same chapters with Tool.XQuery KnowledgeByTag('manual') or Tool.Search), and coding agents working on the repo (start at the root CLAUDE.md, which points to plans/AgentCollaboration.md). Chapters, in reading order: - 01 Operator daily loop - the captain's morning routine: which surfaces to check, in what order. - 02 Platform concepts - what a part, board, agency, site and list ARE, with pointers to the deeper primers. - 03 Agent operating manual - how an agent works inside the city: the tool surface, deltas, and the rules. - 04 Dev and coding-agent setup - boot commands, ports, tooling, and the known traps. - 05 Find your way - the bounded ladder an agent climbs to orient without loading the whole city. - 06 Set up another radar or catalog - which SharePoint mechanism covers which case, in order. - 07 Request a service - the service catalog and the intake procedure for a new request, research above all. - 08 Enrol and run a worker - a seat on another machine: its composition, least privilege, the harness rules.
flowchart LR
  subgraph MANUAL["The Ship's Manual - /board/manual"]
    C0["00 Start here"] --> C1["01 Operator daily loop"]
    C1 --> C2["02 Platform concepts"]
    C2 --> C3["03 Agent operating manual"]
    C3 --> C4["04 Dev setup"]
  end
  OP["Operator"] --> C0
  AG["In-app agents - Tool.XQuery / Tool.Search"] --> C3
  DEV["Coding agents - root CLAUDE.md"] --> C4
Beyond the manual: the full research library (every KnowledgeArticle, 60+) lives at /board/knowledge; the live self-description of the running platform is /about; every board is indexed at /board.
Operator daily loop - driving the shipThe captain's morning routine: the surfaces to check and act on, in order.
The daily loop, in order: 1. The bridge - /observatory?skin=bridge. The TODAY panel answers the morning questions at a glance: is the daily brief fresh, what fires next on the schedule, and the latest eval verdicts. The rest of the bridge shows the goal, instruments and milestone ladder. 2. The daily brief - /board/briefings. The Daily Intelligence brief lands each morning (06:35 UTC cadence, publish-gated by Eval.DailyBrief). One brief per day; if it is stale the TODAY panel says so. 3. Standing orders - /board/schedule. Every declared Schedule with its gate verdict, last-fired and next-fire time, and catch-up flags. This is where you see whether the cadence actually ran. 4. Quality - /board/evals. Declared-eval coverage plus recent verdict history. An honest Fail here is signal, not noise. 5. Health - /board/health. The honest per-agency grading of the living districts. 6. Decisions waiting on you - /approvals (quorum approvals) and the Hub inbox at /sites/Hub. 7. Money - /board/credits (the TEST/LIVE honesty badge) and /board/outcome (cost-per-outcome). Everything else is indexed at /board - notably /board/gaps (what the city says is missing), /board/agencies (the roster), /board/registry (the yellow pages: every tool's mission, inputs and outputs), and /board/quests (suggested next actions). Rule of thumb: the boards are projections of the same spine the agents read - if a board looks wrong, the data is wrong, not the paint.
Platform concepts - what things areThe five nouns that explain the whole platform, with pointers to the deeper primers.
Five nouns explain the platform: - PART - the unit of everything. A declared XML element (Tool, Board, Agency, Schedule, Skill, Workflow, KnowledgeArticle, ...) in the merged spine (SPICE.Web/Config/Parts.xml plus each app's Parts.*.xml), validated against SAF-Parts.xsd at boot. Behavior is declared as data; C# is only the engine that executes parts. - BOARD - a read-only projection of the spine or a live store, rendered through XSLT at /board/{key} with an XML twin at /board/{key}/data. Around 35 exist; /board indexes them. - AGENCY (district) - a crew of agents with recurring, gated work (Wisdom, Research, Academy, Content, Genesis, Strategy, PartsFactory, ...). Declaring an Agency as data creates its district and eval node. - SITE and LIST - the SharePoint-shaped content substrate. Sites hold lists; lists hold typed items; CAML-style queries read them. The Hub site (/sites/Hub) is the operator command center. - EVAL - the anti-theatre instrument. Declared evals gate ships and publishes (for example the daily brief only publishes when Eval.DailyBrief passes); verdicts land on /board/evals. Deeper primers - all on /board/knowledge, do not duplicate them, read them: - KB.PlatformInOneSentence - the elevator version. - KB.SystemState - the narrative state of the system with the honest ALIVE versus PARKED ledger. - KB.SpicePrinciples - the layered-architecture rules every contributor must follow. - KB.AntiPatterns - the numbered anti-pattern ledger; cite by number.
Agent operating manual - working inside the cityFor in-app agents: the tool surface, how to change the city, and the rules that keep it coherent.
How an agent operates inside SPICE: READ before you act - the city is self-describing: - Tool.XQuery - query the merged spine with XQuery 3.1: any part kind, the cycle ledger, or knowledge by tag (KnowledgeByTag('manual') returns this manual). The spine is the single source of truth. - Tool.Search - lexical plus vector search over the KnowledgeArticle corpus when you do not know the exact id or tag. - Tool.FetchKnowledge - resolve live data slots (for example ListView('Issues', limit=5)) declared as KnowledgeRef elements; this fetches CURRENT list rows, a different concern from the static KB articles. - Tool.Advise - synthesized advice grounded in the city's filed wisdom. CHANGE the city only through declared channels: - Emit a ProvisioningDelta - schema-valid XML in the SAF namespace using the closed operation vocabulary. Never invent element names; follow the taught skeleton (see KB.ProvisioningDeltaPattern). - Runtime list writes must prefix the item key with "item:" or the row is invisible to every list view. - Structural change beyond a delta is not yours - raise it via the Hub inbox for the operator. THE RULES (non-negotiable, from the platform DNA): - Declare-first: prompts, templates, thresholds, cron, vocabularies are DATA (parts, settings, knowledge), never code literals. - Reuse the existing part before proposing a new one. - XML inside the platform; JSON only at external boundaries via an XSLT adaptor. - Honest output: never return an empty final silently; an eval must separate infra-error from subject-error. Protocol references on /board/knowledge: KB.AgenticToolUse (tool-use protocol), KB.ReActProtocol (the reason-act loop), KB.AgentProtocolChannels and KB.AgentSurfaceContract (how agents talk to the platform).
source: plans/AgentCollaboration.md
Dev setup - booting, tooling and the trapsFor developers and coding agents: boot commands, ports, offline tools, and the known footguns.
Read plans/AgentCollaboration.md FIRST - it is the canonical brief for any agent touching the repo. This chapter is the environment cheat-sheet. BOOT (always Development, always the explicit port): ASPNETCORE_ENVIRONMENT=Development dotnet run --project SPICE.Web --urls http://localhost:5198 A bare dotnet run boots :5000 with the Echo stub and the wrong environment. Development uses the free local LLM gateway; Production defaults to a PAID provider - keep daily work on Development. OFFLINE TOOLS (query before you grep): - tools/parts-xq.ps1 - XPath/XQuery over the merged part spine; one query beats a 14-file grep. - tools/spice-validate - XSD plus well-formedness over the spine; run after ANY part-XML edit. - tools/spice-edit.ps1 - EDIT the spine by part Id (set Attr=Value, text Child, insert after an Id, replace, remove) as a byte-exact patch of that part only, and diff the spine against git part by part (spice-edit diff). Use it instead of text replacement; SaxonCS-HE has no XQuery Update. - tools/types-map - the C# structure map; also served at /agency/types. - tools/obs-smoke.ps1 - served smoke against :5198. THE TRAPS (each cost a real day; cite the anti-pattern ledger, KB.AntiPatterns, by number): - Manifest boot-rewrite (#29): booting the app (Served tests boot too) rewrites SAF-Site-Manifest.xml. Stage your clean edit with git add BEFORE anything boots, then discard the working-tree churn. Never commit manifest churn. - Shared dev DB: tests and the running app share the Development database. Test cleanup must be key-scoped, never wholesale, or it wipes the operator's data. - Assert the served surface (#23/#25): unit tests bypass XSD and the reader path. A cycle is done only when the WRITE round-trips through the SERVED read (boot, hit the endpoint, see the data). - Razor fallbacks: pages render as an XML model through XSLT; never add a Razor view or keep one as a fallback. A null model is a 404. The deleted list-form fallback read rows with no permission or approval check and showed Pending pages to readers (AGT.73). VERIFY before calling anything done: full test suite green, spice-validate clean, smoke the touched endpoints on :5198, and the served round-trip proven.
source: plans/AgentCollaboration.md
Ship portrait image promptThe declared, token-filled prompt the ShipMap page uses to draw the platform as a generation spaceship. Edit the wording here (not in code) - {{districts}}, {{machines}}, {{crews}} are filled from the live org.
Isometric sci-fi concept art, cutaway view of a colossal generation spaceship: a whole city under a great glass dome (cupola), drifting in deep space with stars outside. Inside the dome, distinct glowing district-buildings, each a busy automated factory with conveyor belts, robotic arms and crews of worker robots moving parts between them. The labelled districts are: {{departments}}. {{machines}} machine types across {{crews}} crews. Warm interior lights, monorail belts between buildings, highly detailed, cinematic lighting, artstation, no text.
SharePoint patterns mined into SPICEWhich SP on-prem patterns this platform already inherits, and why
SPICE is built on deliberate inheritance from SharePoint on-premises mental models, not a clean-slate platform. Patterns already mined: site definitions / blueprints via SAF-Site-Manifest.xml; XSLT-based rendering via wwwroot/XSLT/MasterPage.xslt and Briefing.xslt; CAML query language constrained for safe agent authoring (SAF-Caml.xsd); property bag (IPropertyBagService); web part declarations in blueprints; permissions block in blueprints. The platform philosophy is "XML/XSD/XSLT as DNA": every artifact is schema-validated XML, every transform is XSLT, agents emit XML that is XSD-checked before any state changes. This is the SharePoint-architect mental model translated to a multi-agent harness.
source: plans/AgencyArchitecture.md
SharePoint patterns queued for SPICEWhich SP patterns are not yet implemented and which would help most
Patterns declared in manifests but not yet enforced or fully built: Content Types with real inheritance - a typed schema for list items so agents can emit validated structured rows; the Parent attribute on ContentType is currently advisory only. Features framework - activation-bundle composition; Feature declarations exist but do not activate coordinated parts; mapping to SPICE: a Feature part references skills+knowledge+queries+policy+settings and activates them atomically per department. Event Receivers - reactive surface (ItemAdded/ItemUpdating); maps to declarative EventReceiver parts referencing skills. Site Columns / Field Definitions - reusable typed columns shared across content types. List Templates - reusable list shapes. Term Store hierarchical taxonomy - TaxonomySearchProvider exists but is barely wired in. Search Service / managed properties as a future Target=Search CAML provider. Highest leverage to build next: Content Types + inheritance. It is the foundation that lets typed audit, reports, validated list items, and content-type-aware CAML all become possible.
source: plans/AgencyArchitecture.md
How to add a new PartGuide for extending the SAF Part Library
To add a new part type: (1) define a new complexType in SAF-Parts.xsd that extends p:PartBase via xs:complexContent/xs:extension; (2) add the corresponding element under the PartLibrary root choice; (3) add seed instances to Parts.xml; (4) drop in a new IPartHandler implementation under SPICE.Web/Services/Parts/ and register it in PartServiceCollectionExtensions. The PartLibrary engine never changes - the registry picks up the new handler automatically. To add an instance of an existing type, append a new element to Parts.xml with a unique Id and the loader will pick it up at startup after XSD validation passes.
source: SPICE.Web/Config/Schemas/SAF-Parts.xsd
The platform in one sentenceThe single load-bearing claim every new proposal should be checked against
SPICE is a multi-agent harness where XML is the contract, XSLT is the view, the part library is the registry, phases are the unit of orchestration, deltas are the unit of change, and SharePoint is what makes enterprises adopt all of the above. If a proposal contradicts that sentence, push back before building.
source: plans/AgencyArchitecture.md#section-14
The Registry + Strategy pattern that runs the platformWhy every layer of SPICE is registry-driven and how to extend without touching engines
Every load-bearing layer of SPICE is the same pattern: a Strategy (one class per concrete kind) registered into DI; an engine that resolves the chain at request time and dispatches by element name. There are no switch statements in the engines. The pattern applies in twelve places: IPartHandler (parts), IOperationHandler (provisioning ops), IPredicateHandler (CAML predicates), IOperandResolver (context tokens), IFieldExtractor (per-row-type field access), IQueryProvider (CAML targets like Parts/Audit), and so on. To extend the platform: drop in one new class implementing the Strategy interface, register it in the matching ServiceCollectionExtensions method (one line). The engine sees it on the next request. No engine edits, no switch statements, no precedence ordering to maintain. The DI container is the registry; the engine is just iteration.
source: plans/AgencyArchitecture.md#section-9
Phased briefing pattern (front-loaded context, XSLT-rendered)Why context is computed once per phase and rendered through XSLT instead of streaming
Each phase gets one briefing assembled before the LLM call: a complete AgentBriefing XML doc covering Mission, Hierarchy, SkillsThisPhase, ToolsAvailable, Knowledge, JitContext, Constraints, Handoff. The XML is the source of truth; the rendered Markdown+YAML is just a view produced by Briefing.xslt. Why front-loaded over streaming: deterministic + reproducible (same input -> same XML -> same prompt -> same agent input; hashable for caching), policy-checkable up-front (denies happen before the LLM call), one round-trip per phase (cheaper than tool-calling chatter), audit by construction (the XML is the receipt). Tradeoff: open-ended exploration phases lose vs streaming - the escape hatch is the query/filter skill (CAML engine) so an agent that realizes mid-execution it needs more can pull, not re-prompt. C# never builds prompts by string concatenation; designers tune Briefing.xslt without touching code.
source: plans/AgencyArchitecture.md#section-6
Agents emit deltas; engine appliesThe non-negotiable rule for state changes: schema-validated XML deltas, never direct writes
Agents never mutate state directly. They emit a ProvisioningDelta XML document that conforms to SAF-ProvisioningDelta.xsd. A separate Apply step commits it. Why this shape: reversible (every action is a delta; undo = inverse delta), auditable (the delta XML *is* the receipt; no parallel log), reviewable (humans or another agent can preview before Apply runs; dryRun=true short-circuits), reproducible (replay -> same result), schema-bound (LLMs cannot drift; if the agent emits an op type that is not in the XSD, validation fails closed). Self-bootstrapping: SPICE provisions SPICE - the agency builds new agencies. Implementation: ProvisioningApplier dispatches each child of ProvisioningDelta to the IOperationHandler whose ElementLocalName matches. Adding a new operation type = drop one file under Services/Provisioning/, register one line. After successful apply: hot-reload PartLibrary + DepartmentRoster, mirror Config/ to bin/Debug/.../Config/ (so SafProvisioningEngine sees the change without restart), re-run provisioning. The new department/skill/feature is live immediately.
source: plans/AgencyArchitecture.md#section-7
SPICE anti-patterns (do not do these)Eight specific moves that would fight the platform's design; check proposals against this list
(1) Do NOT redeclare content types/lists/fields/skills inline in SiteBlueprint blocks - reference parts by Id via PartRef. (2) Do NOT add a parallel rendering stack. The Scriban/JSON path that lives alongside XSLT is scope creep, not phase 2; have it consume the same AgentBriefing XML via a different stylesheet, never two sources of truth. (3) Do NOT write to storage from agents. Always emit a delta. If you find yourself wanting to write directly, that is a new operation type that belongs in SAF-ProvisioningDelta.xsd. (4) Do NOT hardcode blueprint or phase names in C#. They live in XML. SiteDefinitionLoader.LoadProvisionedSites used to carry a frozen four-name array (a known wart) - paid in V19.2c: it now discovers SiteBlueprint names from the SAF manifest, so GetAllSiteDefinitions reflects all provisioned portals at startup. The legacy array survives only as a fallback when no IConfigurationLoader is registered. (5) Do NOT inject IStorageProvider into a Singleton (captive-dependency leak; see KB.DiCaptiveDependencyFix). (6) Do NOT auto-apply deltas. Default flow returns the delta + a preview; Apply is explicit. Auto-apply is opt-in per phase, never the default. (7) Do NOT mutate Parts.xml or SAF-Site-Manifest.xml from random places. Only ProvisioningApplier writes them. Other code reads. (8) Do NOT bypass the policy engine. Every skill execution goes through IPolicyEngine.EvaluateSkillExecution first - tests too.
source: plans/AgencyArchitecture.md#section-10
Agent protocols are channels over the XML spine, not parallel stacksHow AG-UI / MCP / A2A map onto SPICE's three agent boundaries; the rule for adopting any new agent-interop protocol
The three open agent-interop protocols map exactly onto SPICE's three agent boundaries, and each is a CHANNEL (a projection/transport) over the one source of truth - the XML part library - never a parallel architecture. This extends the V11.23-V11.26 channel-symmetry matrix (SavedQuery/Tool/Workflow reachable via HTTP + MCP, single payload truth): A2A and AG-UI are simply two more columns, same payloads, different transport. BOUNDARY 1 Agent-Tools/Data = MCP (Model Context Protocol, Anthropic): already core. SPICE auto-generates MCP tools/resources from the part library, ships --mcp-stdio, and exposes Tools + Workflows over MCP (V11.25/V11.26). MCP is the agent surface (see KB.AgentSurfaceContract); it is internal architecture, not an external add-on. BOUNDARY 2 Agent-Agent = A2A (Agent-to-Agent, Google): the external boundary. Internal agent-agent coordination stays part-library-driven (departments, EngageConsultant, cross-dept Tool.InvokeSkill). A2A is the inter-org / inter-vendor seam (how a SPICE agency talks to another vendor's agents); tracked as V13.3 (A2A/ACP bridge). BOUNDARY 3 Agent-User = AG-UI (Agent-User Interaction): the one real gap and the only one with a posture tension. SPICE's human surface is server-rendered XSLT (request/response, SharePoint-style pages, FormView Next-step). AG-UI is for real-time, event-streamed, interactive agent-user UX (chat, streaming tool calls, generative UI). Adopt it DELIBERATELY and only when SPICE wants a streaming/conversational front-end; it is additive (another projection channel) and must not pull the model toward "UI is the truth" (humans consume rendered views, agents manage via MCP/XML - KB.AgentSurfaceContract). THE RULE: any new agent-interop protocol slots into the channel-symmetry matrix as another projection over the part library; it must never become a parallel rendering/coordination stack (anti-pattern #2). MCP done; A2A is the next external boundary worth funding; AG-UI parked behind an explicit "do we want streaming agent UX?" decision.
source: plans/AgencyArchitecture.md
The typed envelope (Agent Contract) - borrow CloudEvents + Schematron + content-negotiation, do not inventThe standards-grounded design for a self-describing work-product envelope: a CloudEvents frame, an xsi:type per-task-type payload, Schematron output guardrails, and content-negotiated representations (md/xml/pdf via XSLT/XSL-FO). The DRY answer to 'envelope whose schema depends on task type + a declared return type + CrewAI-style guardrails'.
THE QUESTION (operator, 2026-05-30): an artifact/manifest envelope that carries BOTH typed (ContentType-validated) and untyped data, whose payload SCHEMA depends on task type (msg/task/crm/erp/manufacturing), with a declared RETURN type (markdown-of-a-ContentType / xml-by-xsd / pdf) and OUTPUT GUARDRAILS (CrewAI-style) - is there already a standard / middleware? ANSWER: yes, one per piece, and they map almost 1:1. Borrow them (DRY, the reuse-first reflex - see KB.N8nWisdom, feedback_investigate_online_before_inventing); do NOT hand-roll. CRUCIALLY the COMBINATION (CloudEvents + Schematron + DITA-style typed-output dispatch over an XML spine) is prior-art-free at the architecture level - so borrowing the pieces IS the differentiator, not a commodity. THE MAP: (1) ENVELOPE FRAME = CloudEvents 1.0 (CNCF, graduated 2024; has an XML format working draft, ns http://cloudevents.io/xmlformat/V1). Borrow the ATTRIBUTE VOCABULARY (type, source, id, datacontenttype, dataschema, subject, time, specversion) - keep our own XSD, not their namespace. `type` = the task-type URI (spice.task.crm.contact-summary); `dataschema` = the per-task-type XSD URI; `datacontenttype` = the return media type. (2) PER-TASK-TYPE PAYLOAD = the xsi:type discriminator (SOAP/CloudEvents-XML) + DITA topic specialization: one XSD complexType per task type, each specializing a base ArtifactPayloadBase; `<Payload xsi:type="spice:CrmPayload">` selects it; the human-readable twin is a TaskType attribute. (3) RETURN/REPRESENTATION = HTTP content negotiation (RFC 9110 Accept) + the +xml structured-syntax suffix (RFC 6839, application/vnd.spice.{ct}+xml) + profile negotiation (RFC 6906/W3C, the profile URI = the XSD namespace) + DITA/DocBook single-source-multi-output: one typed XML source rendered to md/html/pdf BY XSLT (pdf via XSL-FO / Apache FOP - the XSL-native path that closes the Tooling.PdfRender gap). This is exactly the platform's XML-is-truth-XSLT-adapts-to-other-forms principle (feedback_xml_self_describing_xslt_adaptor) generalized; Page.xslt already dispatches by @Type. (4) GUARDRAILS = Schematron (ISO/IEC 19757-3:2025) - the XML-native analogue of CrewAI guardrails. TWO-PASS: XSD validates STRUCTURE (grammar/cardinality/datatype), Schematron validates BUSINESS RULES (cross-field/conditional via XPath asserts, <sch:assert test=...>). Output is SVRL (<svrl:failed-assert>); on failure inject the message into the next phase's BriefingHint and re-run (the V25.7 BriefingBuilder seam) up to a MaxRetries on the Phase. CrewAI parity is exact: output_pydantic = XSD-typed payload; guardrail->(False,error)->retry = SVRL->briefing-hint->rerun; guardrail_max_retries = Phase MaxRetries. Schematron in .NET runs via its XSLT skeleton (we already have an XSLT engine). MINIMAL SHAPE: <ArtifactPackage type=... source=... id=... dataschema=... datacontenttype=... MaxRetries=3><Representations><Representation mediaType=... xslt=.../></Representations><Payload xsi:type=spice:CrmPayload>...</Payload><GuardrailResult status=passed/></ArtifactPackage>. DO NOT BORROW: the full SOAP processing model (mustUnderstand/faults) nor the full DITA-OT toolchain (our XSLT dispatch already covers it); CloudEvents specversion is useful for envelope versioning. THE RULE: a self-describing traveler must carry its schema (the .wsp Solution manifest HAS an XSD; so must ours) - the V25.8 ArtifactPackage shipped schema-less, which this arc corrects. CROSS-DOMAIN ANCHOR: this is the manufacturing TRAVELER CARD - envelope header + work-piece spec (typed payload) + QA gate (SVRL) + rework routing (retry). EXECUTION: arc planned in plans/TypedEnvelope.md (envelope frame -> per-domain payload types -> Schematron guardrails -> content-negotiated representations), structured for multi-agent fan-out (per-domain payload XSDs and .sch files are independent files = parallelizable; the base type + GuardrailValidator engine + conneg endpoint stay in main per feedback_no_subagent_structural_csharp).
source: plans/TypedEnvelope.md
Ship topology - belt is dataflow, bus is hub-and-spoke; multi-server without premature KubernetesThe platform topology decision: the production belt is intra-pipeline DATAFLOW (direct station-to-station, XProc's default connection), the agent bus is inter-agency HUB-AND-SPOKE dispatch - separate concerns, do not merge. Multi-server is possible WITHOUT Kubernetes (scale the stateless monolith on shared SQL, or distribute agencies as MCP/A2A services over the bus); k8s is an ops convenience, not a prerequisite.
OPERATOR QUESTION (2026-05-30): should the belt go through a dispatch hub-and-spoke, and can modules run on different servers / do we need Kubernetes? DECISION. (1) BELT vs BUS - keep them SEPARATE and complementary. The BELT (ProductionLineExecutor) is intra-pipeline DATAFLOW: each station threads its primary output to the next station's primary input directly - which IS XProc's default connection. Keep it direct: fast, deterministic, local; routing every step through a hub adds a network hop and couples dataflow to messaging. The BUS (IAgentMessageBus) is inter-agency/inter-division DISPATCH: hub-and-spoke "route by competency" - that is where hub-and-spoke belongs. RULE: a step that stays WITHIN a pipeline -> the belt; a step that HANDS OFF to another division/agency -> publish to the bus. This is the classic pipeline (orchestration) + service-bus (messaging) split, mapped onto XProc-pipeline + the agency bus. (2) MULTI-SERVER without premature Kubernetes - two paths, NEITHER needs k8s to start: (a) scale the STATELESS MONOLITH horizontally - N copies of SPICE.Web behind a load balancer, shared SqlServer state; handles load; k8s optional (VMs + an LB suffice). (b) distribute AGENCIES as MCP/A2A services - each division runs as its own SPICE instance exposing MCP over HTTP (the HttpMcpClient seam already exists), and the bus routes across them (hub-and-spoke at the inter-agency level, see KB.AgentProtocolChannels); no k8s - separate hosts + a service registry. k8s is an OPS CONVENIENCE for orchestrating many instances, NOT a prerequisite. Start single-process + shared SQL; the MCP + bus seams already make distribution possible later WITHOUT a rewrite - swap the in-process IAgentMessageBus transport for a real broker (RabbitMQ / Azure Service Bus) via a plugin (the engine-extension pattern) when cross-server is actually needed. Remote belt stations are possible (a heavy/specialised station - GPU render, PDF - as a remote MCP tool call; XProc supports remote steps conceptually) but only when a station is heavy enough to earn the hop; default = keep a pipeline run in one process (dataflow locality). RECOMMENDATION: do not distribute yet. Bank this decision, keep the MCP/A2A + bus seams clean, scale the monolith on shared SQL first; reach for distribution (then optionally k8s) only when a real load or isolation requirement appears.
source: plans/SystemEvolution.md
The XML engine + pipeline - SaxonCS-HE for XSLT 3.0 / XQuery, and XProc as the LLM-known pipeline vocabularySPICE adopted SaxonCS-HE 13.0 (free, native .NET, shipped 2026-05-29) behind IAdvancedXmlEngine for real XSLT 3.0 + XQuery 3.1 - the BCL is XSLT 1.0 only forever. XProc the ENGINE is rejected (no native .NET impl, JVM only); XProc the VOCABULARY is borrowed as the pipeline language because LLMs already know it - run on SPICE's own belt, steps powered by Saxon. A standard is self-describing to LLMs the way XML+XSD is to outside consumers.
WHAT (V25.12, 4dde975): SaxonCS-HE 13.0 (NuGet SaxonCS-HE, MPL 2.0, FREE, NATIVE C# - not IKVM, shipped 2026-05-29) gives the WHOLE platform XSLT 3.0 + XQuery 3.1 + XPath 3.1, wired behind IAdvancedXmlEngine (SPICE.Foundation.Xml, no Saxon dep so core depends only on the abstraction) with the impl in SPICE.Plugins.Saxon (engine-extension plugin, isolates the 16 transitive deps) registered by an always-active SaxonPluginModule so LoadSpicePlugins puts it in app-wide DI. System.Xml's XslCompiledTransform stays XSLT 1.0 FOREVER - do NOT repeat the stale "the BCL only does XSLT 1.0 so we can't" reasoning; we can now. THE PRINCIPLE (operator insight): a W3C/ISO STANDARD is self-describing to LLMs - in an LLM-building environment the agents already KNOW XProc / CloudEvents / Schematron, so BORROW the standard's vocabulary as the language (no bespoke format to teach) and EXECUTE on SPICE's own native engine. This is feedback_xml_self_describing_xslt_adaptor aimed at the agent audience. XPROC specifically: the ENGINE is rejected (no native .NET processor - Calabash/Morgana are JVM-only; adopting it = a JVM boundary), but the VOCABULARY is borrowed as the pipeline language (p:xslt / p:validate-with-xml-schema / p:validate-with-schematron / p:identity / p:xquery; ports; the default primary-output->primary-input connection the ProductionLineExecutor belt ALREADY does). Borrow the standard + the DNA, native engine. WHAT NOT TO DO: do not adopt XmlPrime (no NuGet, partial XSLT 3.0, commercial, no XProc) nor the IKVM Saxon wrappers (heavy, redundant now). PDF still needs an FO processor (Apache FOP = JVM, or a .NET HTML-to-PDF lib) - Saxon is XSLT, NOT XSL-FO formatting; PDF stays gated. The hand-rolled SchematronValidator (XPath-1.0, V25.11) stays as the zero-dep fallback until Saxon proves stable in production. USE IT FOR: the guardrail ISO Schematron skeleton (E3), md/html via XSLT 3.0 grouping + xsl:result-document (E4), JSON-for-external via Saxon xml-to-json (XSLT-native, no hand-rolled JSON), the XProc-vocabulary belt steps, and progressively leveling up the XSLT-1.0 projection layer where 3.0 removes complexity. See also KB.TypedEnvelopeContract (the standards map) + KB.AgentProtocolChannels.
source: plans/XmlEngineUpgrade.md
Research agenda - agentic-engineering patterns + the agent-qualification gapOpen research for a future dedicated session; maps candidate patterns to what SPICE already has so the session starts oriented, and names the one genuinely open gap
STATUS: research agenda, NOT decisions. Park here; pick up in a dedicated session. KEY FINDING: most named agentic-engineering patterns are ALREADY covered by SPICE - document the map first so the session does not rebuild what exists, and spends its budget on the one real gap (agent qualification / competency). Evaluate every candidate through the parts-as-truth + channel-matrix lens (see KB.AgentProtocolChannels, KB.AgentSurfaceContract); resist parallel stacks (anti-pattern #2); let N=2 worked examples pull dimensions into existence rather than over-modelling upfront. PATTERN-BY-PATTERN (HAVE vs OPEN): (1) Reusable prompt library per task type - HAVE: Phase + BriefingBuilder + XSLT render prompts FROM parts; a Phase is a typed, composable prompt template per task type, stronger than a flat prompt file because it is schema-validated and data-driven. OPEN: confirm whether an explicit PromptTemplate part adds anything over Phase+XSLT (likely not). (2) Sub-agents for specialization (planner / tester / reviewer) - HAVE: departments + multi-actor rosters + EngageConsultant (V11.6) + cross-dept Tool.InvokeSkill (V11.7) + specialized phases (Studio 6 actors, OodaLoop phase chain). OPEN: formalize a REUSABLE planner-tester-reviewer triad as a Workflow template; today specialization is per-App, not a named reusable shape. (3) Wrapper layer around tools/APIs (one clean interface) - HAVE, effectively done: IToolInvoker + IToolRegistry + MCP auto-generation + channel symmetry; agents call Tools, never raw endpoints. OPEN: none major. (4) Human-in-the-loop propose-then-approve - HAVE, foundational: ProvisioningDelta (agents emit deltas, humans apply), anti-pattern #6 (no auto-apply by default), ContentType.ApprovalRequest + quorum EventReceiver, ApprovedDeltaApplier. OPEN: optional per-phase opt-in auto-apply behind a stricter policy for low-risk tasks (faster action without losing the audit trail). (5) Plan -> execute -> verify predictable shape - HAVE: OodaLoop App, Workflow->Phase chains, Transitions state machine, validation phases (ExecuteValidationPhaseAsync), policy + audit as the verify rail. OPEN: a canonical plan-execute-verify Workflow TEMPLATE any department reuses. THE ONE GENUINELY OPEN GAP (highest leverage) - AGENT QUALIFICATION / COMPETENCY: SPICE conflates AUTHORITY (PartRole, a single linear axis ordered Assistant, Member, Lead, Manager) with COMPETENCY (what cognitive work an agent is actually good at). Cognitive tier lives on the Phase (UsesModel, Mode), not the agent, so a department asking for a role gets a permission level, not a competency guarantee. There is NO mechanism to test/qualify/certify an agent for a position before it works (searched: qualif/competen/certif/audition/assess/eval - absent). And ActorProfile.SkillCatalog (the per-agent allowlist of skills) is ADVISORY - IPolicyEngine.EvaluateSkillExecution never checks it (latent hole: config implies a constraint the runtime does not enforce). SHAPE IF BUILT (data+enforcement, not new engine): cheapest first step = enforce SkillCatalog in IPolicyEngine (turn advisory allowlist into a real one - delivers fine-grained what-an-agent-can-do immediately). Full version = a Talent/Qualification department (the consultancy-agency idea) as a worked-example App: ContentType.Competency + ContentType.Certification (agent x competency x verdict x evidence x expiry) + a qualification Workflow whose Phase runs an eval skill against a candidate and writes a certification row; enforcement stays in the shared policy gate (cross-cutting, never siloed in the site); the reference matrix = a cross-site list-view projection (agent x competency x certified-at), not a new subsystem. CAUTION: do not model 40 competency dimensions upfront; start binary (certified per skill yes/no), add a competency TIER per skill only when a real phase needs Lead-level-vs-Member-level reasoning. FRAMING: this is Pillar 5 (agentic access) done properly - give each agent exactly the breadth its proven competency warrants. Companion analysis was produced 2026-05-25; this article is the durable carrier.
source: plans/AgencyArchitecture.md
Ship-systems criticality - the FMECA lens for prioritising SPICEHow SPICE classifies subsystems and backlog by failure-criticality, borrowing FMECA / IEC 60812 + NASA criticality categories instead of an ad-hoc triage
FRAMING (user, 2026-05-26, "compare this like a spaceship and life-support, is there a standard ISO?"): YES - the discipline is FMECA (Failure Mode, Effects and Criticality Analysis), standardised as IEC 60812:2018 (the 2018 edition added software/service examples + a criticality-matrix method). NASA operationalised it on Apollo (1966) to prevent life-support-class failures. Sibling rigor standards: IEC 61508 (SIL 1-4) and DO-178C (DAL A-E) set how much redundancy/assurance a component must have, scaled to its criticality. Lightweight cousins we had been using informally: MoSCoW and the Eisenhower urgent x important matrix. THE INSIGHT FMECA ADDS that our ad-hoc urgent/important/critical triage blurred: criticality is not one axis. Rank by SEVERITY (what happens to the SHIP if this is absent/broken) x OCCURRENCE (how likely) x DETECTION (would we even notice it failed) = Risk Priority Number. The DETECTION axis is the one SPICE keeps getting bitten on: the V21.4 regression (all 40 blueprints silently down, masked by SQL persistence) was catastrophic precisely because Detection was near-zero. The fixes we banked - fail-loud-in-Dev provisioning, the PROVISION HEALTH smoke line, ArchitectureConventionTests - were us RAISING DETECTION on a Crit-1 subsystem. We were doing FMECA by instinct; this article names it. NASA-STYLE CRITICALITY CATEGORIES APPLIED TO SPICE: Crit 1 (life-support) = ship cannot run or runs unsafely without it: boot + provisioning, the cost gate (runaway spend = mission loss), the competency/policy gate (wrong agent acts), the rate gate (V22.17). Crit 2 (mission) = ship runs but cannot do the work: seat coverage/staffing, the router fallback chain, the response cache (degrades to slow, not broken). Crit 3 (comfort) = quality-of-life, not load-bearing: the visual boards (/organogram, /lifecycle, ...), PDF, extra dashboards. HOW TO APPLY: (1) prioritise the backlog by Severity-of-absence, not by novelty or business polish; raise Detection on every Crit-1 subsystem (fail-loud + health-smoke + convention-test) - low Detection on a Crit-1 system is itself a Crit-1 defect. (2) Put REDUNDANCY where Severity is highest, exactly like spacecraft triple their life-support and not their cupholders. This is why V22.18 deputies are criticality-driven: a deputy (a competency-matched backstop) is generated for departments whose seats carry mission-critical authority (BoundRole >= Lead), flipping those seats single -> Replaceable, while Crit-3 comfort seats are left single (redundancy is not free; spend it where loss stops the ship). v1 LIMITATION (feedback_limitation_as_receipt): Severity is proxied by seat AUTHORITY (BoundRole). The fuller FMECA model is a declared per-seat Criticality (1/2/3) attribute + an Occurrence/Detection score; deferred until a real case needs finer than authority-as-proxy. RELATED: this is the standards-borrowing reflex (feedback_investigate_online_before_inventing) applied to a prioritisation MODEL, not an XSD.
source: plans/AgencyArchitecture.md
Need-to-know knowledge compartments - the military SCI model for agent briefingsWhy agents are briefed on a need-to-know basis, borrowing Bell-LaPadula MLS + SCI compartmentalization, and how the Compartment axis works
FRAMING (user, 2026-05-26, "give ship employees only the KB they need, not all our core ship secret workings; are there army standards?"): YES - this is the U.S. DoD multilevel-security model, formalised as BELL-LAPADULA, the basis of military MLS. Access has TWO INDEPENDENT AXES: (1) CLEARANCE LEVEL - Unclassified < Confidential < Secret < Top Secret, enforced as "no read up". SPICE already has this: Classification (Public/Internal/Confidential) + MinimumRole = the clearance level, applied by IPartLibrary.AvailableTo. (2) COMPARTMENT / NEED-TO-KNOW - TS/SCI (Sensitive Compartmented Information): each compartment is a domain (SIGINT, HUMINT, ...); an analyst cleared Top Secret still only sees the compartments they are READ INTO. SPICE was MISSING axis 2 - which is why every Internal-cleared operator was briefed the platform-engineering KBs (KB.SpicePrinciples, KB.SharePointPatternsMined, ...): a textbook compartmentalisation failure. THE FIX (V22.21): KnowledgeArticle gained a Compartment attribute (default "Platform" - secure-by-default). BriefingBuilder applies a single mandatory reference-monitor check (IsReadInto) to EVERY article entering a briefing, from both the explicit-query and default-bundle paths: a department gets an article iff (a) its compartment is the universal "Operator" compartment (general domain knowledge), (b) the compartment names that department (dept-owned KB), or (c) the department is in the compartment's read-in roster - DATA in Scope.KnowledgeCompartments ({Compartment}.ReadInDepartments), so who-is-cleared-for-what is policy not code. The dogfood Engineering department is read into Platform (it legitimately works ON the ship), so it still gets the architecture KBs; operator departments do not. HOW TO AUTHOR: a domain KB an operator should see = Compartment="Operator" (or name its department); platform/engineering/meta knowledge = leave the default "Platform". RELATED STANDARDS: ABAC (NIST SP 800-162) is the general attribute-based form; this is the standards-borrowing reflex (KB.ShipCriticality borrowed FMECA the same way - see feedback_investigate_online_before_inventing). LIMITATION (feedback_limitation_as_receipt): v1 compartments are flat strings with a per-compartment read-in list; full Bell-LaPadula lattice composition (a clearance dominating a set of compartments) is deferred until a real case needs more than read-in lists.
source: plans/AgencyArchitecture.md
Records retention and auto-declassification - the ISO 15489 + military downgrade lifecycleWhy content types carry a retention/lifecycle policy, borrowing ISO 15489 records disposition + military auto-declassification, and how the Retention grammar + IRetentionPolicy reference monitor work
FRAMING (V22.24, KB.ShipGovernance Pillar 3 - records lifecycle, the platform's biggest SharePoint-governance gap): SPICE records lived forever at their declared classification. Real records management does NOT - it borrows TWO converging standards (DRY, the standards-borrowing reflex - see feedback_investigate_online_before_inventing). (1) ISO 15489 / records management: every record class has a DISPOSITION SCHEDULE - retain for a defined period measured from a basis date, then dispose (destroy) or transfer. NARA/DoD 5015.2 formalise the same retain-then-act schedule. (2) MILITARY AUTO-DECLASSIFICATION (E.O. 13526): classified information is automatically DOWNGRADED after a set interval (typically declassified at 25 years) unless re-marked - the classification is not permanent, it decays on a schedule. These are the same shape as the Bell-LaPadula axes already adopted (KB.NeedToKnowCompartments): there, classification gates WHO reads; here, TIME gates HOW LONG it stays at that classification. THE GRAMMAR (V22.24): ContentType gained an optional <Retention RetainForDays="..." ThenAction="Expire|Downgrade" DowngradeTo="Public" Basis="Created|Modified"/> block (additive init-only, mirrors the V12.0b Transitions shape; absent = the V11.1 "lives forever" contract preserved). After RetainForDays from the Basis timestamp: Expire = due for disposal (ISO 15489 destruction); Downgrade = effective classification drops to DowngradeTo (auto-declassification - never RAISES, never below Public). Inherited down the Parent chain (leaf-wins), same precedence as Transitions/FieldRefs. THE REFERENCE MONITOR (V22.24): IRetentionPolicy (DefaultRetentionPolicy, Singleton) resolves a ContentType's effective policy - declared on the type, inherited from an ancestor, or the org-wide default from Scope.Retention (DefaultRetainForDays/DefaultThenAction - DATA, not code, mirroring how Scope.DataResidency/Scope.KnowledgeCompartments hold their policy). Evaluate(ct, classification, basisTime, now) returns a RetentionVerdict: NotGoverned / Active (within window) / Due (+ Action + the downgraded EffectiveClassification). The disposition decision is a pure static EvaluatePolicy (directly testable, no part library), the same testability seam the other monitors use. This is the fourth governance reference monitor after policy-engine competency, the need-to-know compartment monitor, and the NOFORN data-residency guard. OBSERVABLE (board-as-gate): GET /retention projects the disposition schedule (builder -> XML -> XSLT, the house pattern) - one row per content type with a declared/inherited policy, the org default in the header. Pure part-data so it boots offline (no list items needed). Worked examples seeded: ContentType.Contract retains 2555d (~7y) then Expire (legal/tax record-keeping); ContentType.ArchitectureDecisionRecord retains 365d then Downgrades Internal -> Public (the architectural reasoning becomes historical, safe to publish). CROSS-FEATURE NOTE: auto-declassification COMPOSES with the NOFORN gate (KB.ShipGovernance Pillar 1) - a record that downgrades below the ExternalForbiddenClassifications floor becomes eligible to leave the box, exactly as the military intends (declassified material is releasable). LIMITATION (feedback_limitation_as_receipt): v1 evaluates a record's verdict on demand against an injected timestamp; there is no scheduled job that walks the lists and ACTS on Due records (purge / re-mark). The grammar, monitor, and board are the policy spine; the enforcement job (an ItemAdded/cron sweep that applies the verdict) is the deferred next step, gated on the live-item dimension the read-only board doesn't need.
source: plans/AgencyArchitecture.md
Ship governance model - SharePoint on-prem pillars fused with military classification (Bell-LaPadula/SCI)The unified governance constitution: the five SharePoint on-prem governance pillars, each reinforced by how the US military classification system actually works, mapped to SPICE with the gaps named. Borrowed, not invented (DRY).
PURPOSE (user, 2026-05-26): adopt ONE governance model = the SharePoint on-prem pillars WITH the military need-to-know system inside them; study how each actually worked and reuse it (DRY). KEY INSIGHT: the two traditions are the SAME governance shape, and they reinforce each other - SharePoint gives the enterprise-document mechanics, the military gives the harder access semantics. SPICE keeps SharePoint's structure and upgrades it to the military's strictness where it matters. THE FIVE PILLARS (SP mechanism + military reinforcement -> SPICE): PILLAR 1 - ACCESS CONTROL. SP: permission LEVELS (7 levels, defined at site-collection top, INHERITED down site->subsite->list->folder->item, with break-inheritance) + AUDIENCE TARGETING (target web parts/nav/items to groups). CRITICAL SP CAVEAT (from the docs): audience targeting is VISIBILITY, not security - it hides, it does not deny. MILITARY: three CLEARANCE LEVELS (Confidential<Secret<TopSecret, EO 13526) with no-read-up (Bell-LaPadula) + SCI COMPARTMENTS (need-to-know beyond clearance, segregated by discipline) + DISSEMINATION CAVEATS (NOFORN = no release to foreign/outside parties regardless of clearance; ORCON = originator controls and tracks who holds it). SPICE: clearance LEVEL = Classification + PartRole (inherited via the part hierarchy); COMPARTMENT = KnowledgeArticle.Compartment + the BriefingBuilder reference monitor (V22.21) - and unlike SharePoint we made the compartment a REAL mandatory gate (deny, not just hide), matching the military model. OWED: (a) one shared reference monitor (LEVEL x COMPARTMENT x CAVEAT) reused across ALL surfaces - briefings, web parts, navigation, skill invocation - finishing SP Audience-Targeting parity but as real access control; (b) HANDLING CAVEATS - the NOFORN analogue is now Crit-1 because V22.20 wired a REAL EXTERNAL LLM (Baseten): Confidential or platform-compartment content MUST NOT be sent to an external inference provider. ORCON analogue = originator/department approval before a part crosses a site boundary (partly present as ApprovalRequest + EngageConsultant intents). PILLAR 2 - INFORMATION ARCHITECTURE / AUTHORITY. SP: Content Type Hub (a central site collection is the official source of content types + site columns, PUBLISHED/syndicated to subscribers via timer jobs - replication not immediate). MILITARY: Original Classification Authority - a designated authority defines what is classified and at what level; everyone else inherits/derives. SPICE: the Part Library + Config manifest IS the content-type hub (central source, manifest-syndicated, schema-validated by SAF-Parts.xsd); classification/compartment LABELS are authored centrally and inherited by parts. STATUS: strong - this is SPICE's spine. PILLAR 3 - LIFECYCLE / COMPLIANCE. SP: Information Management Policies per CONTENT TYPE (or list/library) - retention, expiration, auditing, labels, barcodes; auditing logs view/edit/checkin/checkout/delete/permission-change. MILITARY: DECLASSIFICATION schedules - automatic declassification after 10 or 25 years from original classification, with marked exemptions (e.g. 50X1 for sources and methods). Both are the SAME idea: content has a TIME dimension - it expires, downgrades, and every access is logged. SPICE: IAuditLog covers auditing (strong). GAP (the biggest): no retention / expiration / auto-downgrade policy engine. Borrow ISO 15489 (records management) + the military auto-declassification shape: a per-ContentType policy with retain-for / then expire-or-downgrade. PILLAR 4 - SERVICE GOVERNANCE. SP: service applications (Managed Metadata, Search, User Profile) administered centrally, consumed by site collections via PROXIES. MILITARY: central control systems (each SCI program run centrally, accessed under procedure). SPICE: the Hub / service-application pattern (ServicesPortal, channel symmetry, proxy introspection). STATUS: strong. PILLAR 5 - OPERATIONAL GOVERNANCE. SP: quota templates (storage), site locks, self-service site creation policy, sandboxed-vs-farm solutions (Solution Gallery code governance), site-collection audit settings, the written Governance Plan (roles: steering committee, site owners, IT). MILITARY: access logs, two-person control, the security officer role. SPICE: CostBudget + rate gate (V22.17) = quota reframed from storage to SPEND and CALL-RATE (the right quota for an agent ship); provisioning-DELTA + ApprovalRequest = governed self-service creation (agents propose, humans approve - anti-pattern #6); plugins + layered assemblies = the farm/sandbox split; IAuditLog = audit settings; /onboarding + /coverage + /organogram = the living Governance Plan / org view. STATUS: strong. SCORECARD: Pillars 2, 4, 5 strong; Pillar 1 strong on level+compartment, OWED on shared-monitor + handling-caveats (external-LLM NOFORN is Crit-1 now); Pillar 3 (lifecycle/retention) is the biggest gap. PRIORITISED CYCLES (criticality-ordered, KB.ShipCriticality lens): V22.23 external-disclosure caveat (NOFORN - Crit-1, newly urgent post-Baseten); V22.22 generalise the governance reference monitor across surfaces (SP Audience-Targeting parity as real MAC); V22.24 lifecycle/retention policies (Pillar 3 gap, borrow ISO 15489 + military declassification). PRINCIPLE: this whole article is the standards-borrowing reflex (feedback_investigate_online_before_inventing) applied to governance - keep SharePoint's shape, adopt the military's strictness, invent nothing.
source: plans/AgencyArchitecture.md
Architectural decisions and their rationaleConcise log of load-bearing decisions made across SPICE's design cycles, with the alternative considered and why it was rejected
D1: XML/XSD/XSLT as DNA, not JSON/Scriban. Alternative considered: modern JSON+template engine. Rejected because the platform value depends on agent-authored content being XSD-validatable end-to-end, and SharePoint mental-model continuity for enterprise architects. D2: CAML at the agent interface, not XPath strings. Alternative: full XPath. Rejected because XPath strings cannot be schema-constrained the way CAML XML can; agent-authored XPath would need a custom parser/whitelist. D3: Strategy + Registry for every layer instead of switch statements. Alternative: typed switch in each engine. Rejected because each new feature would require engine edits; the registry pattern means the engines are written once. D4: Front-loaded phase context (briefing built before invocation), not streaming/tool-loop. Alternative: progressive tool-calling. Rejected for happy-path determinism, audit, caching, cost; reserved tool-calling as the escape hatch via the CAML query skill. D5: Agents emit ProvisioningDelta XML, never write directly. Alternative: agents call storage APIs. Rejected because indirection through schema-validated XML is what makes LLM output safe to apply. D6: Settings cascade Farm-Department-Phase-Skill-Invocation, last match wins. Alternative: flat global config. Rejected because per-department/per-phase tuning is core to multi-tenant agencies. D7: Content Types via xs:extension and Parent IDREF. Alternative: flat content types. Rejected because SharePoint compatibility and field reuse depend on inheritance. D8: MCP tools auto-generated from SavedQuery parts, not hand-coded. Alternative: separate hand-curated MCP server config. Rejected because two sources of truth would drift; the part library is canonical. D9: Audit log queryable as Target=Audit through the same CAML engine. Alternative: separate reporting API. Rejected because every cross-cutting query surface should compose with the others, not become its own silo. D10: Test project covers the registry-driven layers (xUnit). Added late but locks in correctness as the platform grew broad.
source: plans/AgencyArchitecture.md
How an agent fills out a list formUse Tool.ListSchema to learn the field shape, then emit AddListItem with matching field names. Same JSON also at /sites/{X}/Lists/{Y}/_schema.
When a skill needs to file a list item (ApprovalRequest, Issue, WikiPage, etc.), the agent should NOT guess the field names. Two access modes: (1) FrontLoaded mode - declare <UsesTool ToolId="Tool.ListSchema" Args="site={{department}};list=ApprovalRequests"/> on the Phase, and BriefingBuilder pre-fetches the JSON Schema into the briefing's # Live data block; (2) ToolLoop mode - the agent calls Tool.ListSchema mid-execution when it realises it needs the field shape (progressive disclosure). The returned JSON Schema document carries: title (the list name), x-spice-content-type (the resolved ContentType), x-spice-lineage (inheritance chain), required[] (which fields must be present), properties{} (one entry per field with type + enum + default + x-spice-format hints). Field types map: Text/User/Url/Note -> "string" (Note adds x-spice-format=textarea, User adds x-spice-format=user); Number -> "number"; Boolean -> "boolean"; DateTime -> "string" with format=date-time; Choice/MultiChoice -> "string" with enum array. Same XSLT (wwwroot/XSLT/ListSchemaJson.xslt) drives both Tool.ListSchema and the HTTP endpoint, so the agent surface and the human form are projections of one ContentType - changing a field updates both. After fetching the schema, agent emits a ProvisioningDelta with <AddListItem List="ApprovalRequests" Site="Approvals"> and one <Field Name="Subject">value</Field> per property; required[] determines what's mandatory; default values shown by the schema can be omitted. The applier validates the payload against the resolved ContentType before writing.
source: SPICE.Web/wwwroot/XSLT/ListSchemaJson.xslt
How to extend SPICE (one-liner per axis)The minimum-edit recipe to add each kind of declarative concept
New part TYPE: extend PartBase in SAF-Parts.xsd via xs:extension; add element under PartLibrary root choice; create C# record + IPartHandler under Services/Parts/; register in AddPartHandlers. New part INSTANCE: append element to Parts.xml. New CAML predicate: implement IPredicateHandler in Services/Caml/PredicateHandlers.cs; register in AddCamlQueryEngine. New CAML context token: implement IOperandResolver; register. New CAML target backend: implement IQueryProvider; register. New ProvisioningDelta operation: extend SAF-ProvisioningDelta.xsd; implement IOperationHandler under Services/Provisioning/; register. New department: AddSiteBlueprint delta with optional Feature/PartRef children, then Apply. New skill bound to a dept: AddSkillBinding delta. New MCP tool: append a SavedQuery part. New MCP resource: any new part is automatically a resource. New setting: append a Setting child to a SettingScope, or create a new SettingScope at the level you want. New ContentType: extend an existing one via Parent attribute and reference FieldDefinition parts via FieldRef. New EventReceiver: add an EventReceiver part referencing the skill to fire and the ContentType filter. New Feature (activation bundle): add a Feature part with BindSkill children; activate via ActivateFeature delta or by including Feature children in AddSiteBlueprint.
source: plans/AgencyArchitecture.md#section-11
Self-list Lookup convention for parent-ref fieldsV15.1a/b/c: child-to-header parent-ref fields are Lookup with no LookupSite (defaults to current department). Composite-key parents (BOM, Routing) get a synthetic single-field ID rather than a multi-key Lookup attribute.
Child rows referencing a same-department header (SalesOrderLine -> SalesOrder, InvoiceLine -> Invoice, JournalEntryLine -> JournalEntry, Payment -> Invoice, ProductionOperation -> ProductionOrder, ShipmentNotice -> SalesOrder, BomComponent -> BillOfMaterials, RoutingOperation -> Routing, ProductionOrder -> BillOfMaterials/Routing) use FieldType=Lookup with LookupList set to the header list name and LookupDisplayField set to the header's identifying field (OrderNumber, InvoiceNumber, JournalNumber, BomId, RoutingId, etc.). LookupSite is omitted - ListViewModelBuilder.LoadLookupOptions resolves `targetSite = LookupSite ?? department`, so omitting means "look up against the current department's list of the same name." The "engine-side self-list seam" deferred at V13.6 and queued for V14.7 turned out to be a phantom requirement - the engine already covered it. V15.1a/V15.1b banked the convention as XML across 7 single-field parent-refs. V15.1c resolved composite-key parent-refs (BomComponent + RoutingOperation + ProductionOrder.BomVersion/RoutingVersion) via **synthetic IDs on the header** rather than a multi-key Lookup attribute: BillOfMaterials gained Field.BomId (convention BomId = ProductSku + '-v' + Version, operator-assigned, e.g. WIDGET-A-v1.0); Routing gained Field.RoutingId similarly. Child fields then ride the single-field convention. Why synthetic-ID-on-header over engine-side LookupCompositeKey: KISS - the platform already supports single-field Lookups, so adding the synthetic ID composes with V15.1a/b instead of adding a new attribute + FormView semantics + ListView semantics. Mirrors how every other line CT in the platform works (OrderNumber, InvoiceNumber) and matches SAP/BaaN/NetSuite convention. Storage shape: the Lookup stores the parent row's storage ID (standard Lookup behaviour); FormView and ListView render the LookupDisplayField text via the @Display attribute. CrossModuleRule rewrite cost at V15.1c: the V14.2c Rule.Tm.ProductionOrderReleased CAML `<Where/>` simplified from And(Eq(ProductSku), Eq(BomVersion)) to a single Eq(BomId); ParamRef Name="BomId" picks up ProductionOrder.BomId at event time. When adding a new line-item ContentType: prefer single-field identity on the header (OrderNumber, DocumentNumber, or synthetic-ID if the natural key is composite); declare the child's parent-ref as Lookup with no LookupSite from day one.
source: SPICE.Web/Services/ListViewModelBuilder.cs
Fixing captive scoped-singleton DI bugsThe IServiceScopeFactory pattern for safely consuming scoped services from singletons
If a Singleton service injects a Scoped service directly, the Scoped instance and its dependencies (e.g. DbContext) become captive: held by the Singleton forever, leaking memory and causing thread-safety issues under concurrent requests. Fix: inject IServiceScopeFactory instead, then for each call create a fresh scope with `using var scope = _scopeFactory.CreateScope(); var svc = scope.ServiceProvider.GetRequiredService<TScoped>();`. Cache hot-path read results in the singleton (e.g. ConcurrentDictionary) to avoid scope-creation overhead per call.
source: SPICE.Web/Services/SiteDefinitionLoader.cs
Platform surface contract: agents manage, humans consumeThe platform exposes exactly two surface families to the outside world: typed tool calls for agents (MCP, auto-generated from the part library), and rendered sites/lists/admin for humans (XSLT-projected). Everything else is internal.
This platform is for agents to MANAGE and humans to CONSUME the products. Two non-negotiable surface families flow from that positioning. (1) AGENT SURFACE - typed MCP tool calls. Every Phase, Workflow, Tool, Skill in the part library auto-publishes as an MCP tool with JSON-Schema-derived typed args (cycle X4 + V11.7 wire this through --mcp-stdio). The agent never writes a CAML query, an XPath expression, or an XSLT template; it calls Tool.X(typed-args) and the platform routes through IPhaseOrchestrator. CAML, XPath, XSLT, XSD are PLATFORM INTERNALS - they validate inputs/outputs and drive projections, but the agent's contract is "here is a typed tool, here are typed args, here is typed output". (2) HUMAN SURFACE - rendered web. /sites/X/Lists/Y projections, /admin cards, /agency/* endpoints, all XSLT-projected from the same registry. No CLI. A CLI surface would be a third path that duplicates the agent path (agents already invoke via MCP) and bypasses the human path (humans already see rendered sites); doesn't earn its weight. (3) WHY XML AS THE INNER CONTRACT - LLMs are highly fluent with XML structure (massive HTML/SVG/XHTML training corpus, plus Anthropic's tool-use protocol IS XML-tag-wrapped under the hood). The V11.1 typed-XML output contract leans into this. V11.12 JSON option is a token-cost optimisation, not a fluency win. (4) DOMAIN FRAMING - the platform is enterprise/corporate (Farm -> Division -> Department -> SubDept hierarchy; SiteBlueprint, Hub, Feature - SharePoint-style organisational metaphors), NOT civic/city/P2P. Inter-department communication mediates through IConsultancyService.EngageAsync (with audit + policy + cost gates); same-department cross-actor invocation goes through Tool.InvokeSkill (V11.7). (5) THE COMPOUND DISCIPLINE - every new surface should be an XSLT projection of the part library; if you find yourself writing a switch statement over part kinds in a controller, write the XSLT instead. MCP, ListView, /admin, /agency/graphs (V11.19) all follow this pattern. Future surfaces (terminal TUI, mobile, VS Code extension) all become new XSLTs over the same registry. Banked as anti-pattern #18 candidate.
source: plans/AgencyArchitecture.md
V11.22 plan: SPICE.Apps.Studio - multi-specialist department (OpenSwarm clone)Ship an OpenSwarm-equivalent 8-specialist agency as a single SPICE department, fully manifest-declared. Proves the platform's claim that multi-agent orchestration is a manifest concern, not a framework concern.
STRATEGIC FRAME: OpenSwarm (VRSEN/OpenSwarm) ships 8 specialised agents coordinated by an Orchestrator that never answers directly - Virtual Assistant, Deep Research, Data Analyst, Slides, Docs, Image Gen, Video Gen. Every primitive maps cleanly onto existing SPICE concepts: Orchestrator -> PhaseOrchestrator + Workflow.Steps with PriorOutput chaining; specialists -> ActorProfile parts (V11.3/V11.6); tool catalog -> Phase.UsesTool + IToolInvoker (A1-A10); coordination -> IConsultancyService.EngageAsync (V11.7); multi-provider -> IAgentProviderRouter + cost gate (V8.5a/X5); memory -> vector (V11.10/V11.15) + graph (V11.20); external integrations -> Connector (A8) + McpServer (A9). DELIVERABLE: a new SPICE.Apps.Studio package containing one department blueprint with 8 ActorProfile rows (Orchestrator/VirtualAssistant/DeepResearch/DataAnalyst/Slides/Docs/ImageGen/VideoGen), 8 Phase rows (one per specialist, OutputSchema=ContentType.X + OutputList="Studio/X" so outputs auto-persist via V11.2), 1 Workflow.StudioRequest with conditional-via-PriorOutput step chaining, ContentTypes per deliverable shape (SlideDeck/Document/ResearchReport/DataReport/Image/Video), 8 new Tool parts. PLUS approximately 6 small IToolInvoker C# classes wrapping the generation APIs (SlideDeckToolInvoker -> HTML+PPTX export; ImageGenToolInvoker -> fal.ai; VideoGenToolInvoker -> Sora; DocsToolInvoker -> PDF; DataAnalystToolInvoker -> Python kernel shell-out OR C# DataFrame equivalent). EFFORT: 4-5 cycles, one specialist+tool-invoker per cycle, pattern identical to V11.5 OodaLoop manifest-only approach. ENTRY POINT: users invoke via MCP from any MCP client (claude, cursor, openswarm-style binary) - Tool.RunWorkflow(name="StudioRequest", userTask="make me a pitch deck"). No new CLI binary needed - the MCP catalog is the entry point (KB.AgentSurfaceContract). WHAT SPICE ADDS on top of OpenSwarm equivalence: typed contracts (FieldDefinition validates at briefing time), per-actor cost capping (V8.5a MaxCostUsdPerCall hard cap prevents runaway video gen), audit log (every reformulation, every consultancy engagement, every persistence op logged with cost), policy gates per tool use, persistent memory (vector recall + graph traversal across sessions), plugin gating (operator disables Video Gen via Plugins:fal:Enabled=false). ORDER OF EXECUTION: (1) Studio department blueprint + 3 ActorProfiles + Workflow skeleton + 1 ContentType (SlideDeck); (2) SlideDeck Tool + Phase; (3) Research Phase + DeepResearch ActorProfile; (4) Docs Tool/Phase; (5) Image/Video Tools (gated on API keys). Each cycle ships an operator-visible incremental capability. WISDOM: this proves multi-agent orchestration is a MANIFEST CONCERN on this platform, not a framework concern. The C# surface stays small (just the tool invokers); the agency lives in XML.
source: plans/AgencyArchitecture.md
Channel-symmetry matrix: every invocable part kind needs HTTP + MCP entriesBanked from V11.23-V11.26: SavedQuery, Tool, and Workflow each have both an HTTP entry and an MCP entry. The matrix is the design discipline - any new invocable part kind implicitly volunteers two follow-up cycles to close it. Channels are transports, not formats; channels share one projection helper to keep response shapes contract-not-divergent.
THE MATRIX (as of V11.26): | Part kind | HTTP entry | MCP entry | |------------|------------------------------------------|--------------------------| | SavedQuery | /api/sites/{Dept}/lists/{List}/items | query_* (pre-V11.25) | | Tool | POST /agency/tools/{id}/invoke (V8.x) | tool_* (V11.25) | | Workflow | POST /agency/workflows/{id}/run (V11.24) | workflow_* (V11.26) | THE RULE: any new invocable part kind added to the platform implicitly volunteers two follow-up cycles - one to surface an HTTP entry, one to surface an MCP entry. The matrix is not a recommendation; it is the discipline that ratifies KB.AgentSurfaceContract's claim ("MCP is THE agent surface, rendered web is THE human surface"). A part kind reachable only via internal C# callers does not yet have a complete agent-surface story. SINGLE SOURCE OF PAYLOAD TRUTH: when a second channel ships for an existing surface, REUSE the first channel's projection helper. V11.26's ProjectWorkflowResult routes through the same JSON shape V11.24's WorkflowsController.ProjectJson produces - byte-for-byte same fields, casing, omission rules. A caller switching from HTTP to MCP sees an identical response structure. Channels are transports, not formats; the response contract must not diverge per channel. The pattern: find the existing projection helper, route through it; do not write parallel JSON construction. CONTENT NEGOTIATION (HTTP side): XML default, JSON via Accept header. Same pattern V8.x ListsApiController established; V11.24 WorkflowsController follows it. application/problem+xml or +json on errors. application/xml is the canonical contract because the platform's XSD validation runs on the same body that human-facing list forms post. CONTEXT THREADING: the X-Invoking-Department header (HTTP) and invokingDepartment argument (MCP) thread V11.23a's service-agency routing identically. Same field name on PhaseRequest / WorkflowRunRequest; same {{invokingDepartment}} OutputList templating. The platform contract spans channels. COUNTER TO ANTI-PATTERN #17 at the agent-surface layer: not just "every write path needs a read path" but "every internal invocable needs an external surface in both channels." Worth filing as #18 if a future cycle ships an invocable part kind without both channels. THE COMPOUNDING: the matrix is reflexive. Each cell relies on the parts in the column working correctly. If a new query language gets added (don't), three cells need updates. If a new wire format gets added (rare), every cell in two rows gets a new format projection. The matrix is the source-of-truth for "what does this surface have to do".
source: plans/AgencyArchitecture.md#section-13.9.6
Query surfaces: REST + CAML + MCP SavedQuery preferred; no GraphQLArchitectural decision: the platform's preferred query-and-invocation surfaces are REST (XML/JSON, Accept-negotiated) for inter-system, MCP typed tools for agents, and rendered web for humans. CAML is the internal query language and stays internal. GraphQL is deliberately NOT added - it would be a parallel resolver layer over the same data the typed-document spine already covers.
THE DECISION TREE (when a new use case needs a query/invocation surface): | Use case | Preferred surface | |--------------------------------|--------------------------------------------------------------------| | Agent invocation | MCP tool_* / workflow_* (V11.25 / V11.26) | | Agent query | MCP SavedQuery parts (named, parameterised, declarative) | | Agent inline query (escape) | MCP query_caml (raw CAML XML, fallback when no SavedQuery covers) | | Inter-system write | POST /api/sites/.../items OR /agency/workflows/.../run | | Inter-system read | GET /api/sites/.../items?... with Accept: application/xml or json | | Human-facing UI | rendered web at /sites/... (XSLT projection of typed data) | | Operator power-user | curl against the HTTP surface (no separate CLI) | | Catalog/portfolio aggregation | CrossSiteList web part (one XML union view, no resolvers) | WHY NOT GraphQL: (1) The typed-document spine is contract-first, not field-graph-first. ContentType + FieldDefinition already define typed shapes; XSD validates request bodies; Accept negotiation projects to wire format. GraphQL's value proposition ("ask for exactly these fields") is solved upstream of the wire by ContentType definitions. GraphQL would be a parallel resolver layer over the same data. (2) No N+1 join problem. SPICE's list-and-relationship surface is already flat per ContentType, with cross-site joins handled by CrossSiteList web parts that project a single XML union. The schema-driven projection is the join; no resolver tree is needed. (3) Resolver explosion is the GraphQL tax. Every new ContentType / FieldDefinition would need a resolver registered. The platform's principle is "schema is data, not code" - adding a field is a manifest edit, never a code edit. GraphQL would invert that. WHY CAML STAYS INTERNAL (per KB.AgentSurfaceContract): Agents never see raw CAML except through the explicit query_caml escape hatch. The preferred path is SavedQuery parts, which declare typed parameters projected to JSON Schema for MCP. CAML, XPath, XSLT, XSD are platform internals - they validate inputs/outputs and drive projections, but the agent's contract is "here is a typed tool, here are typed args, here is typed output". REST clients NEVER see CAML; they see typed list endpoints whose query-string params project to CAML internally. CONCRETE GUIDANCE WHEN ADDING A NEW SURFACE: ask "does this need a new ContentType, or just a new way to project an existing one?" If new ContentType -> manifest edit + the existing surfaces light up. If new projection -> write the XSLT/Accept-handler. NEVER invent a parallel query language; NEVER hand-write a JSON Schema that could be derived from a FieldDefinition. THE ONE DNA, SIX PROJECTIONS PATTERN: one ContentType part + its FieldDefinitions becomes (1) XSD for validation, (2) XML wire format for V11.1 typed agent output, (3) JSON wire format for V11.12, (4) JSON Schema for MCP inputSchema (V11.25 hand-rolled today; auto-projection queued), (5) XSLT views for human-facing list/form pages, (6) OpenAPI documentation (deferred until an external SDK consumer exists). Each projection is derivable - never hand-maintain them in parallel.
source: plans/AgencyArchitecture.md
Tests-mock-everything can hide real bugs for cyclesV11.27's first real end-to-end orchestrator run surfaced an XSLT-resolver bug latent since V11.4. Every prior test mocked BriefingBuilder, so the real .Load(uri) call path that breaks on Briefing.xslt's xsl:import was never exercised. Pattern: prefer at least one integration smoke per cycle that exercises the real DI graph, not just the mocked one.
THE INCIDENT: V11.4 added <xsl:import href="MermaidWorkflow.xslt"/> to Briefing.xslt. BriefingBuilder.RenderXslt called XslCompiledTransform.Load(string) which uses an XmlThrowingResolver by default - this refuses external URIs and silently blocked the sibling-file load at runtime. The XSLT compile threw "Resolving of external URIs was prohibited" the moment any phase orchestrator actually rendered a briefing. WHY IT WENT LATENT 23 cycles (V11.4 -> V11.27): every test that exercises PhaseOrchestrator wires a StaticBriefingBuilder or similar mock that returns a pre-built BriefingResult without calling XslCompiledTransform.Load. So 1300+ tests passed; the moment V11.27 unblocked the first real end-to-end orchestrator run via HTTP/MCP, the bug surfaced immediately. WHY THE STARTUP-TIME SMOKES MISSED IT: each prior cycle's smoke ritual booted SPICE.Web and probed /about. /about renders through ListViewModelBuilder + AdminViewModelBuilder XSLTs - not Briefing.xslt. The XSLT file got compiled lazily on first use, which only happens when PhaseOrchestrator.RunAsync runs against a real workflow request. Schedule.Cron and EventReceiver workflows could have surfaced it earlier but didn't fire in test environments. THE PATTERN: tests-mock-everything is great for unit-test speed and isolation, but the BriefingBuilder collaborator is too central to mock universally. At least ONE end-to-end integration test per cycle that exercises the real BriefingBuilder against the real Briefing.xslt would have caught V11.4's regression at V11.4 commit time, not V11.27. THE FIX (V11.27 commit 43db1de): BriefingBuilder.RenderXslt now wires an XmlUrlResolver + explicit XsltSettings(enableDocument:false, enableScript:false) - same safety posture (no doc()/script extension), just allows sibling-file xsl:imports to resolve. GENERAL GUIDANCE: when introducing a cross-collaborator feature (V11.4 added an xsl:import; the resolver behavior changed at the XSLT layer), prefer an integration test against the real composition root over a unit test against a mock. The cost of one slow integration test is much less than 23 cycles of latent regression. Related: feedback_smoke_catches_xsd already banked for part-XML XSD bugs; this is the parallel lesson for XSLT-include bugs. EXTENSIONS TO ANTI-PATTERN #18 CANDIDATE (broader formulation): mocking-by-default at the boundaries where bugs actually live ("the real BriefingBuilder", "the real PhaseOrchestrator + real Briefing.xslt", "the real provisioning applier") leaves a class of bugs invisible until production. Worth filing.
source: plans/AgencyArchitecture.md
ContentType + FieldDefinition + FormView IS the InfoPath layerThe platform's InfoPath analogue. Every ContentType auto-renders as a typed form via the V8.x schema-driven FormView.xslt - typed inputs per FieldType (Choice -> select, Lookup -> dropdown populated from the linked list, DateTime -> date picker, Note -> textarea). V12.2 layers BaaN-style 'Next step' buttons on the same form via Transitions block. No new form designer, no parallel template system.
THE CLAIM: SPICE doesn't NEED a separate InfoPath-like form designer because the platform's typed-XML spine already IS the InfoPath analogue. ContentType = InfoPath template (the form definition) FieldDefinition = InfoPath data binding (typed columns) FormView.xslt = InfoPath renderer (display + edit + new modes) Choice / Lookup / etc. = InfoPath control types Transitions block = InfoPath workflow tasks (V12.2) WHAT YOU GET BY DECLARING A CONTENTTYPE: 1. New form /sites/{Dept}/Lists/{List}/New typed input per FieldType 2. Display form /sites/{Dept}/Lists/{List}/Item/{Id} typed display + V12.2 Next-step buttons 3. Edit form /sites/{Dept}/Lists/{List}/Edit/{Id} typed input + Save 4. List view /sites/{Dept}/Lists/{List} typed columns + sort + filter 5. JSON Schema /api/sites/{Dept}/lists/{List}/_schema for external API + MCP 6. XSD /api/sites/{Dept}/lists/{List}/_schema.xsd for SOAP-style XML callers 7. REST CRUD /api/sites/{Dept}/lists/{List}/items with XSD validation 8. MCP query query_caml + per-SavedQuery typed tools ALL of those are projections of ONE manifest declaration. Add a FieldRef, every projection lights up. CONCRETE EXAMPLES: ContentType.BusinessPartner (V12.1) Form at /sites/Common/Lists/BusinessPartners/New renders: Title (text input, required), Lei (text input, max 20), Gln (text input, max 13), RoleCode (dropdown populated from Common/BusinessPartnerRoles), CountryCode (dropdown populated from Common/Countries), DefaultCurrencyCode (dropdown populated from Common/Currencies), DefaultPaymentTermCode (dropdown populated from Common/PaymentTerms). Zero per-ContentType code; all derived from FieldDefinition declarations. ContentType.DemoTask (V12.2) Same form shape PLUS a 'Next step' button row driven by <Transitions/>. Click 'Start' -> POST /advance?to=InProgress -> status flips -> form re-renders with next available transitions. Pure XSLT extension; no JavaScript framework. WHY THIS BEATS A DEDICATED FORM DESIGNER: - Zero parallel tech stack. Designers maintain XML; the same XML drives every other channel (HTTP REST, MCP, agent-typed-XML output). - Forms version with the manifest. A ContentType edit affects every form everywhere. - LLM-fluent. Agents reason about the typed schema directly; no form-designer-specific knowledge needed. - No drift between form and API. The form posts the same shape the API accepts; XSD validates both. NEW FORMS RECIPE: 1. Declare FieldDefinitions for any new typed columns (or reuse existing). 2. Declare a ContentType with FieldRefs. 3. Declare a List with that ContentType on a SiteBlueprint. 4. (Optional) Add a <Transitions/> block for BaaN-style status workflow. Done. Visit /sites/{Dept}/Lists/{List}/New. WHAT THIS REPLACES: - InfoPath / SharePoint Forms / Power Apps custom forms: not needed. - JSON-Schema-driven form builders (Formik / Rjsf / etc.): not needed - we generate JSON Schema FROM the FieldDefinition, never hand-write it. - SOAP form definitions (FormBuilder etc): not needed - XSD is auto-derived. Banked 2026-05-21 (V12.2 worked example shipped). Anti-pattern check: never invent a parallel form designer; always extend ContentType + FieldDefinition.
source: SPICE.Web/wwwroot/XSLT/FormView.xslt
V12 horizon plan: ERP foundation layered on typed-XML spineThe 11-cycle V12 horizon ships a complete ERP foundation: ISO/UN-CEFACT/GAAP taxonomies + master data + BaaN-style state-machine workflow + UBL-aligned operational modules. Solid taxonomy first; modules second; standards-conformance throughout.
STRATEGIC FRAME: The strongest leverage the platform has for ERP isn't the runtime; it's typed XML against world-standard schemas. UBL invoices, PEPPOL eDelivery, ISO 20022 financial messages are ALL typed XML validated against XSD. Mirror their shapes as ContentTypes and every typed-document-spine receipt the platform already has (V11.1 validation, V11.2 auto-persist, V11.24 HTTP entry, V11.25/V11.26 MCP) lights up against world-standard interop FOR FREE. ROADMAP (foundation first; operational second; debrief last): V12.0a Taxonomy foundation - 7 typed termsets + 67-row essentials seed. ISO 4217 (Currencies), ISO 3166-1 (Countries), ISO 639-1 (Languages), UN/CEFACT Rec 20 (UoM), GAAP root account-type categories, plus platform- defined PaymentTerms + BusinessPartnerRoles. SPICE.Apps.Erp.Tc package. Ships da8ba49. V12.0b <Transitions/> seam - State-machine block on ContentType. XSD + part record + IContentTypeResolver helpers. No new behaviour; the seam V12.2+ consume. Ships 404dfd4. V12.1 Master data - 6 ContentTypes: BusinessPartner (LEI/GLN/VAT), MasterItem (UNSPSC/UoM), Address (ISO 19160), BankAccount (IBAN/BIC), ExchangeRate, ChartOfAccount. Lookups bind to V12.0a termsets. Ships d49e25f. V12.2 FormView Next-step - InfoPath-style typed form gains BaaN-style status buttons reading <Transitions/>. POST /advance?to=X endpoint. Ships c9b1f97. V12.3 MCP advance bridge - Tool.AdvanceListItem IToolInvoker. Surfaced via V11.25 bridge as tool_advance_list_item. Channel symmetry on Transitions. Ships f5a93f5. V12.3.5 Bank + debt - V12 horizon banked in Section 13.10; KBs filed (InfoPathAnalogue + ErpFoundationPlan). ListsApiController.Create now persists ContentType into row JSON. V12.4 SPICE.Apps.Erp.Td - Distribution worked example: SalesOrder (Draft-Approved-Picked-Shipped-Invoiced-Closed), PurchaseOrder, ShipmentNotice. Manifest-only. V12.5 FiscalCalendar + Tax - V12.1 deferred extensions needed by Finance. Plus a Schedule.Cron job for ExchangeRate daily snapshots. V12.6 SPICE.Apps.Erp.Tf - Finance worked example. Invoice ContentType declared via OutputSchema='Schemas/UBL/UBL-Invoice-2.1.xsd' so agent-emitted invoices are PEPPOL-compliant BY CONSTRUCTION. V12.7 SPICE.Apps.Erp.Ti - Items / Inventory operational module. StockMove with Open-Confirmed-Done transitions. V12.8 SPICE.Apps.Erp.Tp - Project worked example. V12.9 Hub portfolio - /sites/Hub/Lists/AllSalesOrders etc cross-site projection. Same pattern V11.23c shipped for Studio. V12.10 Debrief - V12 horizon close; lessons banked. PACKAGE NAMING (BaaN/Infor LN convention): SPICE.Apps.Erp.Tc = Common (master data + termsets) SPICE.Apps.Erp.Ti = Items / Inventory SPICE.Apps.Erp.Td = Distribution (Sales + Purchase + Warehouse) SPICE.Apps.Erp.Tp = Project SPICE.Apps.Erp.Tf = Finance SPICE.Apps.Erp.Tm = Manufacturing (optional) ERP-practitioner-fluent at the folder/package level; LLM-fluent at the ContentType level (ContentType.SalesOrder not ContentType.TdSalesOrder). STANDARDS ADOPTED: ISO 4217 Currency codes ISO 3166-1 Country codes ISO 639-1 Language codes UN/CEFACT Rec 20 Unit-of-measure codes GAAP/IFRS Account-type root categories ISO 17442 LEI (Legal Entity Identifier) GS1 GLN Global Location Number ISO 13616 IBAN ISO 9362 BIC / SWIFT ISO 19160 Postal address shape UNSPSC Item classification (top 2 levels seeded) UBL 2.4 Universal Business Language (V12.6) PEPPOL BIS Billing 3.0 (V12.6 via UBL) WHAT THIS BEATS: - 'Implement an ERP module first then bolt on standards later' approach. Foundations of every classic ERP failure are master-data-shaped: currency mismatch, UoM ambiguity, cross-system customer dedup, invoice format wars. Solving them at the schema layer makes downstream modules trivially compliant. - GraphQL / custom query languages. See KB.QuerySurfaceDecision - the typed-XML spine + REST + MCP cover every use case. - SaaS-ERP-form-builder thinking. See KB.InfoPathAnalogue - every form is a projection of one ContentType declaration.
source: plans/AgencyArchitecture.md
V12 horizon close - 4-module ERP receipt + standards conformance + cross-module engagementWhat V12.0a through V12.10 delivered, the three banked lessons (N=4 modules-as-manifest receipt, mid-horizon refactor pays compound interest, Apps-stay-Core-only under cross-module pressure), and compounding signals into earlier feedback memories.
V12 HORIZON CLOSE. Ten cycles shipped from V12.0a (foundation taxonomy seed) through V12.10 (this debrief). Four operational ERP modules (Td Distribution, Tf Finance, Ti Inventory, Tp Project) live on a single shared V12.0 foundation with zero per-module engine code beyond an empty AppModuleBase subclass. WHAT WAS PROMISED AT V12.0a: Standard XSDs ARE the foundation. Mirror UBL / ISO 20022 / PEPPOL as ContentTypes and every typed-document-spine receipt (V11.1 validation, V11.2 auto-persist, V11.24 HTTP, V11.25/V11.26 MCP) lights up against world-standard interop for free. See KB.ErpFoundationPlan for the 11-cycle plan filed at V12.3.5. WHAT WAS DELIVERED: V12.0a Taxonomy foundation (7 ISO termsets + 67-row seed) V12.0b Transitions state-machine seam on ContentType V12.1 Master data (BusinessPartner / Item / Address / BankAccount / ExchangeRate / ChartOfAccount) V12.2 FormView Next-step button reads Transitions V12.3 MCP Tool.AdvanceListItem (channel symmetry) V12.3.5 Bank wisdom + ListsApiController ContentType-on-create V12.4 SPICE.Apps.Erp.Td (Sales/Purchase/Shipment) V12.5 FiscalPeriod + TaxCategory (Finance prep) V12.6 SPICE.Apps.Erp.Tf (UBL-aligned Invoice/Payment/JournalEntry) V12.7 SPICE.Apps.Erp.Ti (StockItem/StockMove/BinLocation) V12.7.5 IItemAdvanceService + ContentTypeStatusResolver extract V12.7b Cross-module engagement (Td PO.Received -> Ti StockMove) V12.8 SPICE.Apps.Erp.Tp (Project/Task/Budget) V12.9 Hub ErpPortfolio cross-site projection V12.10 This debrief NUMERIC RECEIPTS: - 4 operational module packages, all Core-only csproj. - 13 typed operational ContentTypes added across Td/Tf/Ti/Tp. - 2 master-data extensions in Tc (FiscalPeriod, TaxCategory). - ~220 tests added across V12 cycles total (1314 -> 1537). - 1 latent platform bug surfaced + banked: Parts.<ISO 639-1>.xml triggers .NET satellite-resource detection. V12.7 caught it via live smoke; fix is WithCulture=false on EmbeddedResource. - 1 architectural debt cycle paid mid-horizon (V12.7.5). - 0 new framework classes for the operational modules - the N=3 rule held; the V12 foundation alone carries the load. THREE LESSONS BANKED AT V12.10 (Section 13.10.5): 1) N=4 RECEIPT VALIDATES MODULES-AS-MANIFEST. N=1 (Td) could be luck. N=2 (Tf) could be intentional design. N=3 (Ti) surfaced latent platform bugs - that's the real test. N=4 (Tp) shipped in a fraction of the time of Td because the pattern is reflex. Operational ERP modules are pure manifest data on the V12.0 foundation. 2) MID-HORIZON REFACTOR PAYS COMPOUND INTEREST. V12.7.5 extracted IItemAdvanceService + ContentTypeStatusResolver between V12.7 and V12.7b. Then V12.7b's cross-module listener shipped in 45 lines instead of 150+ duplicated lines. Receipt: refactor BEFORE the second caller exists, not after. 3) APPS STAY CORE-ONLY EVEN UNDER CROSS-MODULE PRESSURE. V12.7b's IItemAdvancedListener landed in SPICE.Core.Events (next to V9.10 IItemEventListener) rather than SPICE.Foundation.Events, so Ti subscribed without escalating to a Foundation reference. V9.5 invariant held under stress. N=2 reinforcement of anti-pattern #16 (honest exceptions over silent drift). COMPOUNDING INTO EARLIER FEEDBACK MEMORIES: - KB.ChannelSymmetryMatrix gained a third concrete implementation (V11.25 Tool, V11.26 Workflow, V12.7.5 advance service). - feedback_typed_xml_spine gained a sixth receipt (V12.6 UBL proves standard XSDs ride the spine identically to platform CTs). - anti-pattern #15 (additive shape over rename) landed on V12.7.5's optional advancedListeners ctor param - V12.7b activated fan-out without breaking V12.3 tests. WHAT'S QUEUED (V13.x candidates): - V12.6b: vendor UBL-Invoice-2.1.xsd as embedded resource + wire Phase.OutputSchema for agent-emitted PEPPOL-compliant invoices. - V12.7c: OnHandQuantity recompute on StockMove.Done; negative-stock policy guard; picking-strategy automation. - V12.8b: cross-module Tp.Task.Reference -> Td.SalesOrder.OrderNumber Lookup promotion; line-item ContentTypes. - V13.x: SPICE.Apps.Erp.Tm (Manufacturing) if/when N=5 is needed. - ExchangeRate Schedule.Cron + FX provider plugin (V12.5b).
source: plans/AgencyArchitecture.md
V13 horizon close - native vs connector + dogfood as backlogWhat V13.0 through V13.1c delivered, the three banked lessons (integrate-natively-not-import, Schedule+agent vs Schedule+deterministic cadence fork, dogfood the backlog as platform data not docs), and what's still owed for V14+.
V13 HORIZON CLOSE. Four cycles shipped from V13.0 (platform Atlas + V13 backlog dogfooded as Tp/ProjectTasks rows) through V13.1c (rollover + Kanban + Hub Jira card). V12 closed with a working ERP foundation; V13 turns the platform inward - instead of integrating with external SaaS, it models the SaaS shapes natively. WHAT WAS PROMISED AT V13.0: The V13 backlog moves out of plans/EnterpriseRoadmap.md and lives as Tp/ProjectTasks rows on the same machinery operators use for their work. Operator-visible status flips via V12.2 FormView Next-step or V12.3 MCP tool_advance_list_item; cross-site rollup via V7.3 CrossSiteList on the new /sites/Hub/Pages/PlatformAtlas page. "Dogfood as transparency." WHAT WAS DELIVERED: V13.0 Platform Atlas + V13 backlog dogfooded (cb62cfb) V13.1a SPICE.Apps.Tracker - native Jira-equivalent App (d19cf05) V13.1b SprintCadenceHostedService - deterministic cadence (a9074df) V13.1c Rollover + Kanban + Hub Jira card (cec685e) NUMERIC RECEIPTS: - 1 new App package (SPICE.Apps.Tracker), Core-only csproj following V12 ERP module shape. - 2 new typed ContentTypes (Sprint, Epic) + extension of ContentType.Issue in-place (5 new fields + Transitions block + 3 new IssueStatus choices). - 1 new platform contract added to SPICE.Core (IListItemEnumerator) with SqlServer adapter in SPICE.Infrastructure + Noop fallback in SPICE.Web. - 1 new ListsController view branch (?view=kanban) + 1 new XSLT file (Kanban.xslt) for 8-column kanban projection. - 1 new HubPortal page (TrackerPortfolio) with 4 CrossSiteList web parts. - ~42 tests added across V13 cycles total (1547 -> 1589). - 1 new anti-pattern banked (#18 - don't build inbound connector when platform can model natively). THREE LESSONS BANKED AT V13.1c (Section 13.11.2): 1) INTEGRATE NATIVELY, DON'T IMPORT. V13.1's backlog row originally read "First external connector: Jira/Linear ticket import". One sentence of mid-session redirect reshaped it to "make our own Jira on our platform way". V13.1a then shipped a working Jira-equivalent in one cycle with zero new engine C#. The build-vs-import calculus reverses on the platform side: native shape compounds against every typed-XML spine receipt the platform has accumulated. Filed as anti-pattern #18. 2) SCHEDULE + AGENT VS SCHEDULE + DETERMINISTIC - BOTH LOAD-BEARING. OodaLoop V11.5/V11.7 = Schedule + Workflow + Phase + agent provider (reasoning cadence). Tracker V13.1b = per-App IHostedService + Timer + deterministic logic (plumbing cadence). Both run on the V11.5 SchedulerHostedService lineage; the choice is per-feature. Date arithmetic doesn't need an LLM. Also: when XSD constraints force a fake Workflow reference for documentation-only Schedule parts, skip the Schedule part - anti-pattern #13 ("declared but un-read") stays dodged. 3) DOGFOOD THE BACKLOG AS PLATFORM DATA, NOT PLATFORM DOCS. V13.0's V13Backlog.xml seed reads as the V13 roadmap; the seeder applies it to Tp/ProjectTasks on first boot. Operator-visible cycle status surfaces at /sites/Hub/Pages/PlatformAtlas (the same surface operators use for ERP views) - nobody opens plans/*.md in production. The advance machinery is identical for both ERP and platform-roadmap rows: V12.2 FormView, V12.3 MCP advance, V13.1c rollover. One set of mechanisms, two domains. The platform plans its own next moves with the same surface ERP operators use for orders. COMPOUNDING INTO EARLIER FEEDBACK MEMORIES + ANTI-PATTERNS: - anti-pattern #15 (additive shape over rename) gained three new receipts in V13.1c alone (optional IListItemEnumerator? ctor arg + default RolledOverIssueCount=0 on TickResult + optional GetService vs GetRequiredService). - anti-pattern #16 (don't widen Apps-Core invariant silently) strengthened: IListItemEnumerator added to SPICE.Core, adapter in SPICE.Infrastructure, Noop fallback in SPICE.Web; SPICE.Apps.Tracker stays Core-only. - feedback_typed_xml_spine gained a seventh receipt (Sprint/Epic CTs + Issue extension all typed XML; Kanban reads same ListView XML model). - feedback_smoke_catches_xsd_bugs gained an eighth receipt (V13.1a Feature Id= vs Name= caught by smoke before commit). - KB.ChannelSymmetryMatrix - the V13.1 native-shape pattern reframes the matrix: inbound import is anti-pattern #18 territory; outbound webhook (V13.2) is symmetric integration. WHAT'S QUEUED FOR V14+ (or remaining V13.x candidates): - V12.6b: vendor UBL-Invoice-2.1.xsd + wire Phase.OutputSchema (closes V12.6 standards-conformance promise). - V13.2: outbound webhook listener (POST to operator URL on Transitions.To=X) - the symmetric companion to V13.1's native-shape pattern. - V13.3: A2A/ACP bridge plugin (Google A2A + IBM ACP - specs still moving, ship behind feature flag). - V13.4: real-LLM end-to-end smoke harness (blocked pending ANTHROPIC_API_KEY). - V13.5: listener observability + performance (IItemAdvancedListener fan-out telemetry). - V13.6: line-item ContentTypes (SalesOrderLine + PurchaseOrderLine + InvoiceLine + JournalEntryLine). - Listener-write carve-out for anti-pattern #11 at N=3 (V12.7b + V13.1b both bypass policy gate as system-internal listeners; name the boundary explicitly when a third instance ships). - Kanban POST /advance (drag-and-drop status flips - V13.1c shipped read-only). - Foundation-only rollover telemetry in /admin (NoopListItemEnumerator degrades silently today).
source: plans/AgencyArchitecture.md
V17 horizon close - standards interop as projection, not engineWhat V17.0 through V17.6 delivered, four patterns banked (standards-receipt N=4, curated-subset XSDs, round-trip integrity, sub-cycle absorption), prediction grading, and V18 strategic-theme pick (employee portal recommended).
V17 closed in a single session (seven cycles, commits 58facc3 through 65dcb7c). Tests 1873 -> 1940 (+67). KnowledgeArticles 40 -> 43 (KB.V171BpmnVendoring + KB.V174PizzaOrderWorked + this debrief). DELIVERED: - BPMN 2.0 export at /agency/bpmn/export?workflowId=X (V17.1+V17.2) - BPMN 2.0 import at POST /agency/bpmn/import (V17.3) - DMN 1.3 export at /agency/bpmn/dmn-export?decisionTableId=X (V17.5) - N=1 worked example: Camunda pizza-order BPMN imports cleanly (V17.4 subset-compliant fixture); full-Camunda file rejected honestly via XSD gate (V17.1b candidate scope concrete) - Standards-vendor receipt count now N=4 (UBL + CIQ + BPMN + DMN) - V16.5b BusinessPartner self-scope absorbed (Field.BpSku + PartyScopeField=Sku); V16.6b ShipmentNotice SupplierSku absorbed. Only V16.5b PartyRelationship bidirectional remains deferred (V17.5b) FOUR PATTERNS BANKED (Section 13.17.1): (1) STANDARDS-VENDOR RECEIPT COMPOUNDS AT ONE CYCLE PER SURFACE. Five-piece pipeline (XSD + Phase + XSLT + builder + endpoint) shipped four times now: V12.6b UBL Invoice (Tf), V15.3 OASIS CIQ Party (Tcm), V17.1 BPMN Process (platform), V17.5 DMN Decision (platform). PhaseOrchestrator.ResolveSchemaPath probes both Config/Schemas/ (platform-wide) and AppContext.BaseDirectory/ (App-package vendored) so vendoring location is operator choice, not architectural commitment. Phantom-check discipline now N=3 (V15.3 + V16.1 + V17.1). (2) CURATED-SUBSET XSDs WIN OVER FULL OMG BUNDLES. V17.1 BPMN20.xsd is 140 lines; V17.5 DMN13.xsd is 180 lines. Each uses the official OMG namespace so files open in viewers; round-trip integrity is better served by a tight subset because permissive validation would let unknown constructs pass that import can't actually map to typed parts. V17.4 live receipt: Camunda's userTask was rejected via per-element validation message ("expected: startEvent, endEvent, task, exclusiveGateway, sequenceFlow") - clean operator signal + concrete V17.1b candidate. (3) ROUND-TRIP INTEGRITY AS LOAD-BEARING CLAIM. Setting the claim explicit at horizon-open (V17.0 prediction #3) shaped both projections symmetrically. V17.2 emits task/@implementation=PhaseId; V17.3 reverse-projects to Step/@PhaseId. V17.3 fallback paths (no "p_" prefix, no "Phase." prefix, missing @implementation) keep import tolerant of external Modeler files while staying lossy-by- design on platform-specific annotations. (4) SUB-CYCLE ABSORPTION FOR SMALL CARRY-OVERS. V17.5 absorbed V16.5b (BusinessPartner) + V16.6b (ShipmentNotice) alongside DMN export. Criterion: (a) unambiguously additive shape, (b) main item doesn't depend on absorbed, (c) absorbed work total <= ~20 LOC XML or ~50 LOC code. V16.5b PartyRelationship bidirectional explicitly deferred to V17.5b because it needed multi-field PartyScopeField syntax warranting its own cycle (criterion (a) broke). PREDICTION GRADING (5-for-5 on shape; cost calibration tightened): V17.1 BPMN vendoring sub-2-hour - CONFIRMED. V17.2 projection XSLT bigger than V17.1 - CONFIRMED (~280 LOC vs ~50). V17.3 import symmetric modulo loss - CONFIRMED, loss explicit. V17.4 pizza-order surfaces 2-3 gaps - CONFIRMED exactly (userTask + serviceTask + camunda: namespace). V17.5 absorbs V16.5b/V16.6b cleanly - CONFIRMED with one calibration (2 of 3 absorbed; third deferred). Cost calibration banked: actual ~0.3-0.7x pre-V11 estimates. Refines V15.5+V16.7's 0.2-0.5 with V16+V17 N=2 receipts. V18 STRATEGIC-THEME PICK (Section 13.17.3): Recommended: POLE A - N=3 portal generalisation (employee portal). Build SPICE.Apps.HR + EmployeePortal with Timesheet + LeaveRequest + EmployeeProfile CTs. EmployeeProfile follows the V17.5 BusinessPartner self-scope shape (PartyScopeField= "EmployeeId" or similar). Compounds V14.7 DecisionTable (leave-approval routing) + V12.0b Transitions (approval state machine). Operator-visible. ~5-7 cycle-units. Alternative: POLE B - V17 follow-ups (V17.1b OMG bundle + V17.5b PartyRelationship + DMN BKMs + BPMN DI). Less operator-visible. ~4-6 cycle-units. Alternative: POLE C - BusinessObject lifecycle hooks (V12 follow-up). Adds explicit OnAdvance/OnApprove/OnReject event callbacks per Transition. ~5-7 cycle-units. V17 SINGLE-SENTENCE FRAME: V17 wrapped the platform's declarative process engine in a four- standards interop layer (UBL + CIQ + BPMN + DMN), confirmed round-trip integrity end-to-end with one worked example, and banked the standards-as-projection-not-engine claim with N=4 receipts on a five-piece pipeline that the next cycle per standard just instantiates.
source: plans/AgencyArchitecture.md
V17.4 Camunda pizza-order N=1 worked example - happy path + V17.1b gapsN=1 receipt that V17.1+V17.2+V17.3 round-trip and that real Camunda Modeler files surface concrete V17.1b candidates (userTask, serviceTask, camunda: extensions).
V17.4 ships TWO pizza-order BPMN fixtures + tests proving the V17 horizon's load-bearing claim across two file shapes: (1) PizzaOrder.bpmn - curated to stay within V17.1's BPMN20.xsd subset (bpmn:task only, no Camunda extensions). V17.3 imports cleanly into a 5-step Workflow.PizzaOrder with PhaseIds Phase.PizzaTakeOrder, Phase.PizzaPrepare, Phase.PizzaBake, Phase.PizzaDeliver, Phase.PizzaCollectPayment. Operator-droppable into Parts.xml; would execute end-to-end if those Phases existed (V17.4 doesn't add them - Phase authorship is a platform-side responsibility, not BPMN-interop scope). (2) PizzaOrderCamunda.bpmn - same workflow modeled with bpmn:userTask + bpmn:serviceTask + camunda:assignee / camunda:type / camunda:topic / camunda:formKey extensions. Shape Camunda Modeler emits by default. V17.3 currently REJECTS this file via the V17.1 XSD validation gate ("element 'userTask' not declared in BPMN20 subset"). This is the honest receipt that the V17.1 curated subset is narrower than full OMG BPMN 2.0; the V17.1b candidate is to swap to the full bundle (Semantic.xsd + BPMNDI.xsd + DC.xsd + DI.xsd + xlink-2003-12-31.xsd) which models: - bpmn:userTask, bpmn:serviceTask, bpmn:scriptTask, bpmn:manualTask, bpmn:businessRuleTask, bpmn:sendTask, bpmn:receiveTask - bpmn:boundaryEvent + bpmn:timerEventDefinition (timer task escapes) - bpmn:parallelGateway, bpmn:inclusiveGateway (the V14.2c CrossModuleRule grammar is currently sequential) - bpmn:callActivity + bpmn:subProcess (composition shape) V17.0 PREDICTION #4 GRADED (Section 13.16.2 #4 - "V17.4 pizza-order import surfaces ~2-3 BPMN constructs the platform doesn't yet model"): CONFIRMED with the exact predicted shape. PizzaOrderCamunda surfaces userTask + serviceTask (2 task-type constructs) + camunda: namespace extensions (the platform-specific annotation channel). V17.1b candidate is now a concrete deliverable (swap-XSD-then-extend-projection); V17.5 may absorb it alongside the DMN export work. CAMUNDA EXTENSION SEMANTICS V17.3 LOSES on import (would need V17.1b + a Phase.BpmnAnnotations block to round-trip): - camunda:assignee="${userVar}" - SPICE expresses via Phase.RequiresRole + Settings cascade + IActorContext - camunda:expression="#{javaExpr}" - SPICE expresses via CrossModuleRule.Match + DecisionTable - camunda:type="external" + camunda:topic - SPICE expresses via Phase.UsesTool args - camunda:formKey - SPICE expresses via FormView.xslt + ContentType The "everything Camunda does, SPICE expresses differently" mapping is itself the receipt that the platform's typed-XML spine already covers BPMN's semantic surface; BPMN-as-serialisation is the only V17 deliverable, not BPMN-as-engine. WORKED EXAMPLE LIVE PATH (when V17.1b lands, opening the V17.1 XSD up to userTask/serviceTask): curl -X POST -H "Content-Type: text/xml" --data-binary @PizzaOrderCamunda.bpmn http://host/agency/bpmn/import -> 200 OK with a saf:Workflow element carrying Id="Workflow.PizzaOrderCamunda" + Name="Pizza Order (Camunda)" and Camunda extension attrs landing on a Phase.BpmnAnnotations block (V17.1b shape, not yet shipped). Operator drops Workflow XML into Parts.xml + adds the Phase.PizzaX parts that the imported Steps reference. V17.4 STAYS HONEST: imports the subset-compliant file successfully + documents the rejection of the Camunda file. Two fixture files + 6 anchor tests + this KB. N=1 worked example proves BPMN interop is real for the V17.1 subset and surfaces V17.1b as the next deliverable when operator needs real-Camunda-file import.
source: SPICE.Web.Tests/Fixtures/BPMN/PizzaOrder.bpmn
V17.1 BPMN 2.0 XSD vendoring - phantom-check confirms V12.6b/V15.3 patternV17.1 ran the phantom-check on BPMN vendoring: V12.6b UBL + V15.3 OASIS CIQ patterns carry over with zero new engine code. Curated minimal-subset BPMN 2.0 XSD vendored; full OMG bundle deferred to V17.1b.
V17.1 ran the V16.1-receipt phantom-check on BPMN 2.0 XSD vendoring. Question: do V12.6b UBL + V15.3 OASIS CIQ vendoring patterns cover BPMN, or does it need a separate engine rail? PHANTOM-CHECK FINDING: V12.6b/V15.3 pattern covers BPMN exactly. PhaseOrchestrator.ResolveSchemaPath already probes BOTH (a) ContentRoot/Config/{path} for platform-internal schemas AND (b) AppContext.BaseDirectory/{path} for App-package vendored schemas. Phase.OutputSchema=Schemas/BPMN-2.0/BPMN20.xsd wires the rail; XmlSchemaSet loads + validates. Zero new code. VENDORING LOCATION DECISION: SPICE.Web/Config/Schemas/BPMN-2.0/, NOT an App package. Rationale: - BPMN is a platform-wide standard (V17.2 projection XSLT walks Workflow + DecisionTable parts across MANY App packages: Tcm/Td/Tf/Tp/etc.). UBL was Tf-specific (Invoice); CIQ was Tcm-specific (Lead/Opportunity); BPMN is process-engine-wide. - Centralised at SPICE.Web/Config/Schemas/ matches the V12.6b "platform-internal schemas" path probe (V12.6b/V15.3 used App-package path). - V17.2 XSLT lives at SPICE.Web/wwwroot/XSLT/ (platform-wide); pairing the XSD next to the projection target keeps the lookup consistent. XSD SCOPE - curated minimal subset: Hand-crafted single-file BPMN20.xsd covering the constructs V17.2 projection XSLT emits: bpmn:definitions, bpmn:process, bpmn:startEvent, bpmn:endEvent, bpmn:task (with @implementation = SPICE Skill ref), bpmn:sequenceFlow (with conditionExpression for CrossModuleRule.Match labels), bpmn:exclusiveGateway (for DecisionTable XOR routing). WHY NOT THE FULL OMG BUNDLE: - The OMG BPMN 2.0 spec ships 7+ XSDs with deeply recursive substitution groups (Activity -> Task -> ServiceTask etc.) and abstract bases. Authored for full-fidelity BPMN tooling; more permissive than V17 needs. - Round-trip integrity for the V17.2/V17.3 export/import pair is better served by a tight subset that names exactly the constructs we model. Permissive validation (full OMG) would let an unknown BPMN construct through that V17.3 import couldn't actually map to a typed part. - The subset uses the OFFICIAL BPMN namespace (http://www.omg.org/spec/BPMN/20100524/MODEL) so .bpmn files exported from SPICE open correctly in bpmn.io, Camunda Modeler, and any compliant BPMN viewer. V17.1B CANDIDATE: when V17.4 surfaces a real Camunda BPMN file that uses constructs this subset doesn't model (boundary events, timer events, parallel gateways, call activities, sub-processes), swap in the full OMG bundle. Until then, the subset gives us: - V12.6b/V15.3 vendoring shape with one file instead of seven - Faster validator surface (one XSD vs seven imports) - Honest receipt that V17 only claims to round-trip what V17.2 emits THIRD STANDARDS-VENDOR RECEIPT (after V12.6 UBL + V15.3 CIQ). Pattern compounds: Schemas/UBL/maindoc/UBL-Invoice-2.1.xsd -> Phase.IssueInvoice (Tf) Schemas/OASIS-CIQ-v3/xPIL.xsd -> Phase.IssueOpportunity (Tcm) Schemas/BPMN-2.0/BPMN20.xsd -> Phase.IssueBpmnProcess (platform-wide) V17.1 SUB-2-HOUR CLOSE confirms the V16.1 phantom-check discipline pays off again - investigate before designing, ship the verification cycle as a receipt-plus-trivia delta. Banked next to the V16.1 receipt as "phantom-check before designing is now N=3 (V15.3, V16.1, V17.1) - promoted to default expectation for any horizon row that smells like vendoring or contract addition."
source: SPICE.Web/Config/Schemas/BPMN-2.0/BPMN20.xsd
V16 horizon close - external client portal as manifest concernWhat V16.0 through V16.7 delivered, three patterns banked (phantom-check, N-level resolution, cross-App composition), prediction grading, and V17 strategic-theme pick (BPMN compatibility).
V16 closed in a single session (eight cycles, commits e23c154 through bac7084). Tests 1780 -> 1868 (+88). KnowledgeArticles 36 -> 40 (KB.V161PortalAuthSeamCheck, KB.V165PartySkuCoverage, KB.V166VendorPortal, plus this debrief). DELIVERED: - V16.4 ClientPortal at /sites/ClientPortal with 6 lists across Tcm + Tf (Leads/Activities/CommunicationLogs/Quotes/Invoices/ PartyRelationships), PartySku-scoped per actor. - V16.6 VendorPortal at /sites/VendorPortal with 2 lists across Td + Tf (PurchaseOrders/SupplierInvoices), SupplierSku-scoped. - Six related opt-in seams: IActorContext.PartySku (V16.2), OidcAuthOptions.PartyClaim (V16.2), ContentType.ExternalSubmittable (V16.3), FieldDefinition.ReadOnly + Hidden + Description-tooltip (V16.3b), ContentType.PartyScopeField (V16.5), manifest <List PartyScopeField/> (V16.6). All opt-in additive shape; 88 pre-V16 CTs + 455 pre-V16 FieldDefs unchanged. THREE PATTERNS BANKED (Section 13.15.1): (1) Phantom-requirement check before designing. V15.3 + V16.1 now N=2 receipts; promoted from "new pattern" to "standard discipline." Every horizon-opener seam row earns one verification step before code. V16.1 found 3 phantom + 1 real; sub-2-hour cycle banked the V16.2 recipe. (2) N-level resolution as compounding override seam. V16.5 shipped single-level override (CT.PartyScopeField); V16.6 surfaced multi-party-CT case (UBL Invoice serving both portals) and extended to three-level (List > CT > convention). Anti-pattern #15 (additive shape over rename) compounded six ways in one horizon. (3) Cross-App composition is a manifest concern. V16.4 + V16.6 reuse the same Invoice CT through different party axes, zero per-App wiring. The typed-XML spine reaches across App boundaries because parts live in a global registry. Future portals = manifest edits + zero-to-N opt-in attrs. PREDICTION GRADING (5-for-5 on shape; estimates off 2-5x cheaper): V16.1 auth phantom - confirmed with 1 real calibration (PartySku). V16.2 row scope via CAML - shape confirmed, layer wrong (projection not CAML); KISS won. V16.3 FormView XSLT-touch - wrong layer; XSLT already covered Display vs Edit branching; cost was contract plumbing. V16.4 N=1 pure-XML - confirmed exactly; 5x cheaper than estimated. V16.6 N=2 ran (V16.4 shipped clean); surfaced one small new seam (per-list override) that compounds. Banked recalibration: at current cadence, multiply build estimates by 0.2-0.5 against pre-V11 anchors. Single-session unless 15+ cycle-units. V17 STRATEGIC-THEME PICK - BPMN compatibility: - V15.5-deferred candidate; now clear V17 pick. - BPMN is a serialisation layer over V14.2c CrossModuleRule + V14.7 DecisionTable + V15.4 Match wildcards (= the existing declarative process engine). Anti-pattern #18 spirit: integrate natively, not build connector. - Three prior receipts compose into V17: V12.6 UBL XSD vendoring, V15.3 OASIS CIQ vendoring, V16.6 cross-App composition. - Operator-visible: BPMN-projection XSLT exports existing Workflow + DecisionTable as bpmn.io / Camunda Modeler-readable .bpmn files. Auditors and process-design teams inspect the same workflows operators authored as XML. V17 cycle sketch: V17.0 Horizon opener + V17Backlog.xml. V17.1 Vendor BPMN 2.0 XSDs + Phase.OutputSchema validation rail. V17.2 BPMN-projection XSLT (Workflow + Steps + Transitions -> BPMN <process>). V17.3 BPMN-import endpoint (.bpmn -> Workflow + Phase + DecisionTable + CrossModuleRule). V17.4 N=1 worked example (Camunda pizza-order import + run). V17.5 V14.7 DecisionTable BPMN export + V16.5b/V16.6b cleanup sub-cycle. V17.6 Horizon debrief. DEFERRED (V17.5 or later): - V16.5b BusinessPartner self-scope (needs stable Sku column + "My Profile" portal list). - V16.5b PartyRelationship bidirectional view (FromPartySku OR ToPartySku). - V16.6b ShipmentNotice SupplierSku for VendorPortal inbound shipment tracking. - Employee portal (V17 alternative if BPMN over-budget; V18 otherwise) - needs new Timesheet + LeaveRequest CTs. V16 single-sentence frame: "V16 turned three V14.5 anticipated hooks into an operationally multi-tenant portal surface - manifest-only - and banked that external-portal addition is a manifest concern, not an engine concern, with six opt-in seams composing across CT + Field + List + Actor."
source: plans/AgencyArchitecture.md
V16.6 VendorPortal N=2 receipt + per-list PartyScopeField overrideN=2 generalisation of the V16.4 ClientPortal shape against a vendor (supplier) audience. Banks the per-list override mechanism that lets the same multi-party CT (UBL Invoice) serve different portals with different party axes.
V16.6 ships the N=2 portal generalisation receipt: VendorPortal mirrors V16.4 ClientPortal shape against a different audience (suppliers rather than customers). Together V16.4 + V16.6 demonstrate the external-portal shape is a manifest concern, not a per-app concern. VENDORPORTAL (DeptCode=VEN, Theme=CorporateBlue): - PurchaseOrders -> ContentType.PurchaseOrder (Td) - SupplierInvoices -> ContentType.Invoice (Tf, per-list override) Both lists read-only (CT-level ExternalSubmittable defaults false). Suppliers track their POs + invoice statuses; PO/invoice creation stays operator-driven through Td/Tf workflows. V16.6 MECHANISM - per-list PartyScopeField override: <List Name="SupplierInvoices" ContentType="ContentType.Invoice" Versioning="true" PartyScopeField="SupplierSku" /> Three-level resolution in ApplyPartyScope + BuildItemForm: (1) Manifest <List PartyScopeField="X"/> (V16.6 most specific) (2) ContentType.PartyScopeField (V16.5 CT-level) (3) "PartySku" convention (V16.2 fallback) Why the override matters: UBL Invoice carries BOTH cac:AccountingSupplierParty (SupplierSku) AND cac:AccountingCustomerParty (CustomerSku). V16.5 set the CT-level default to CustomerSku for ClientPortal. Without V16.6's list-level override, VendorPortal would either need (a) a duplicate "SupplierInvoice" CT (heavyweight, splits the UBL invoice across two schemas) or (b) ClientPortal forgoes Invoice scoping (LEAK). Per-list override is the right seam - same CT, different axis per portal context. V16.6 CT OPT-INS: ContentType.PurchaseOrder (Td) - PartyScopeField="SupplierSku" at CT level. No per-list override needed for the PurchaseOrders list because the CT name aligns with the only realistic portal context (suppliers). CROSS-APP COMPOSITION RECEIPT: V16.4 ClientPortal: Tcm + Tf parts (6 lists) V16.6 VendorPortal: Td + Tf parts (2 lists) Both reuse the same Invoice CT (Tf) but project different party-scope axes. Demonstrates parts live in a global registry; SiteBlueprints reach across App boundaries; per-list override teaches the same multi-party CT to serve different portals. Useful precedent for V17 employee portal or any future external surface that needs to project an existing CT through a new party axis. DEFERRED (V16.6b candidates if a real driver surfaces): - ShipmentNotice (Td) on VendorPortal: has SnCarrierSku, no SupplierSku. Vendor inbound-shipment tracking needs either Field.SnSupplierSku added OR a new "GoodsReceipt" CT modeling the operator's receiving side. Skipped from V16.6 to keep the N=2 receipt focused. - Employee portal: timesheets + leave CTs DON'T EXIST in the platform yet. V17 candidate alongside the V15.5-deferred BPMN engine. - Bidirectional party visibility (PartyRelationship From OR To): V16.5 deferred item still open; multi-direction view becomes relevant if a future portal needs it (employee portal "my reports and my manager" might). - Vendor-side write paths (AcceptPurchaseOrder transition, supplier-uploaded shipment confirmation): V17+ if the read-only V16.6 surface earns its weight. RECIPE FOR FUTURE PORTAL CYCLES: Adding portal N to an existing CT set is now a manifest-only operation: (1) Audit each list's party axis (SupplierSku, CustomerSku, FromPartySku, etc.). (2) If the axis is the CT's natural single party hook, set ContentType.PartyScopeField at the CT-level once. (3) If the same CT serves multiple portals with different axes (multi-party CTs like UBL Invoice), keep the CT-level default for the most common case and override per-list for the others. (4) Set ExternalSubmittable on the CTs the portal needs to accept new submissions on (default read-only). (5) Ship the SiteBlueprint. No engine code.
source: SPICE.Web/Services/ListViewModelBuilder.cs
V16.5 PartySku hook coverage audit + ContentType.PartyScopeField overrideAudit of every V16.4 ClientPortal CT's PartySku hook coverage and the V16.5 fix for two real leaks (Invoice + PartyRelationship).
V16.5 audited the six V16.4 ClientPortal CTs (Lead, Activity, CommunicationLog, Quote, Invoice, PartyRelationship) for V14.5 PartySku hook coverage. The V16.2 ApplyPartyScope filter literally looks for a field named "PartySku" via the PartyScopeFieldName const; when absent, the filter skips and external clients see every row (LEAK). AUDIT FINDINGS: | CT | Field name | Status | |---------------------|------------------------|-------------------| | Lead (Tcm) | PartySku | OK (V14.5) | | Activity (Tcm) | PartySku | OK (V14.5) | | CommunicationLog | PartySku | OK (V14.5) | | Quote (Tcm) | PartySku | OK (V14.5) | | Opportunity (Tcm) | PartySku | OK (V14.5, off-portal) | | Invoice (Tf) | CustomerSku (UBL) | LEAK fixed V16.5 | | PartyRelationship | FromPartySku/ToPartySku | LEAK fixed V16.5 | | BusinessPartner (Tc)| no Sku field today | DEFER (not on portal) | V14.5 hooks paid off for 5 of 6 portal-relevant CTs; the other two needed the V16.5 override below. Honest grade: V14.5's "every workflow CT carries PartySku" was 5/7 accurate; the 2 outliers used domain-aligned names (UBL CustomerSku for Invoice; CIQ-style From/To pair for PartyRelationship). V16.5 FIX - ContentType.PartyScopeField attribute: <ContentType ... PartyScopeField="CustomerSku"/> (Invoice) <ContentType ... PartyScopeField="FromPartySku"/> (PartyRelationship) Defaults to "PartySku" when omitted, so all V14.5 Tcm CTs continue working unchanged (zero breakage). V16.2 ApplyPartyScope + V16.2 BuildItemForm both honour the override. V11.4 additive init-only shape (init { get; init; }). XSD adds optional xs:string attribute. DEFERRED: BusinessPartner (Tc) does NOT have a Sku-name field today; identifier is Title. Setting PartyScopeField="Title" would technically work but Title is a display name, not a stable identity. Defer until V16.5b adds Field.BpSku as a stable identity column + adds BusinessPartner to ClientPortal as "My Profile" list. Two-step shape: identity column first, then portal exposure. PartyRelationship.ToPartySku visibility: V16.5 picks FromPartySku as canonical (the party that initiated the relationship sees it). V16.5b candidate: bidirectional visibility - external client sees relationships where they are EITHER From OR To. Requires either OR-predicate support in ApplyPartyScope or a new convention (e.g. PartyScopeField="FromPartySku|ToPartySku" CSV). Deferred until V16.6 N=2 portal surfaces the use case (vendor portal likely needs From-only view; employee portal likely needs both). RECEIPT FOR FUTURE CYCLES: When adding a new ContentType to a portal blueprint, audit-and-declare: (a) is there a single field name that represents "which BusinessPartner this row belongs to"? If "PartySku", convention covers it. If anything else, declare PartyScopeField="X". (b) is the row scoped by exactly ONE party, or two? V16.5 supports one; multi-party rows wait for V16.5b. (c) is the row external-submittable (V16.3 ExternalSubmittable="true") or read-only? Two orthogonal axes; declare both. Composes with V14.5 Classification (role-based field visibility) and V16.3 ExternalSubmittable (CT-level form-mode gate) and V16.3b per-field affordances. Four V16 attributes now compose: PartyScopeField (V16.5) + ExternalSubmittable (V16.3) + ReadOnly/Hidden/Description (V16.3b) - all opt-in additive shape per anti-pattern #15.
source: SPICE.Web/Services/ListViewModelBuilder.cs
V16.1 phantom-requirement check on portal auth seamV16.1 verified before designing: OIDC plugin + RoleMapper + HttpContextActorContext already compose for external clients via configuration alone. The real V16.2 seam is PartySku binding to IActorContext, not authentication.
The V16.0 horizon-opener row for V16.1 said "verify Plugins.Auth.Oidc + IActorContext + RoleMapper already compose for external-client auth. Magic-link only if OIDC per-tenant config is unmaintainable." V16.1 ran the check. THREE phantom findings + ONE real finding: (1) PHANTOM: Authentication itself. SPICE.Plugins.Auth.Oidc.OidcAuthPluginModule activates on Auth:Authority and wires Cookie + OIDC schemes via standard ASP.NET Core. Authentik / Auth0 / Okta / Keycloak / Azure AD all work by configuration only (OidcOptions docstring lists them). External clients sit in the operator's existing IdP (or a federated trust), authenticate via the same OIDC flow, and present a different group claim. No new code; no magic-link plugin. (2) PHANTOM: Role taxonomy. PartRole has four levels (Manager > Lead > Member > Assistant). RoleMapper.DeriveRole maps claim values to one of those four; unknown groups fall back to Assistant (the lowest authenticated role). External clients map to Assistant via an OidcOptions.RoleMapping entry like "Assistant": [ "external-clients" ]. Adding a fifth "External" PartRole would be premature - per anti-pattern #15 (additive shape over rename), defer the fifth role until V16.6+ surfaces a behaviour the four-role model can't express. Until then, "external client" is "Assistant role + PartySku scope". (3) PHANTOM: HttpContextActorContext routing. Reads ClaimsPrincipal from IHttpContextAccessor, projects to ActorId / DisplayName / Role / IsAuthenticated. ActorId pulls "sub" claim with NameIdentifier fallback; DisplayName tries preferred_username / name / email. Same shape will pick up external-client claims with zero change. (4) REAL: PartySku binding to IActorContext. IActorContext exposes ActorId, DisplayName, Role, IsAuthenticated - no PartySku. V14.5/V14.6/V15.2/V15.3 anchored every external-visible CT (Lead, Opportunity, Activity, Quote, CommunicationLog, PartyRelationship) on a PartySku Lookup against Common/BusinessPartners, BUT no claim-to-PartySku resolution exists today. This is the V16.2 seam. Recommended V16.2 shape (banked here so V16.2 doesn't re-decide): ADD one nullable string PartySku property to IActorContext. HttpContextActorContext reads a configured claim (default "party_sku") with a SubClaimLookup fallback (resolve via Common/BusinessPartners.ExternalUserSubject column - V16.2 may need to add this column to BusinessPartner). OidcOptions gains PartyClaim = "party_sku" with a default and PartyLookupColumn = "ExternalUserSubject" with a default. IListViewBuilder + IItemFormBuilder consume IActorContext.PartySku for PartySku-scoped CAML Eq() injection. Alternatives rejected: (a) Sibling IActorPartyScope service - extra interface for one field; consumers already inject IActorContext, so the field rides for free. (b) Stamp PartySku as a Claim and let CAML predicate handlers read claims - too implicit; making it explicit on IActorContext keeps the typed contract honest. External-client OIDC config recipe (operator copy-paste): set Auth:Authority to the IdP; set Auth:RoleMapping:Assistant to include the external-clients group; set Auth:PartyClaim to the claim that carries the BusinessPartner Sku (or use Auth:PartyLookupColumn to resolve via sub claim if the IdP doesn't expose Sku directly). No platform code change. V16.1 grade for prediction Section 13.14.2 #1 ("V16.1 auth is a phantom-requirement check, not a build cycle"): CONFIRMED. V16.1 ships as a verification cycle + KB filing + sub-2-hour close. V16 sizing therefore drops from 8-12 to 7-11 cycle-units (still two-session, but tighter).
source: SPICE.Foundation/IActorContext.cs
Agentic chat tool-use guidanceAppended to an actor's system prompt when it has tools, so it ACTS via its tools instead of only describing - and never overpromises capabilities the city lacks (e.g. real email). Read by ChatAgentToolInvoker.
You have real tools available. When the operator asks for something one of your tools can do - for example building an agency or a division - USE the tool with their request rather than only describing it. Your build tools PARK a proposal for the operator's approval; nothing is built without their yes, so it is safe to act. After a tool runs, tell the operator plainly what you parked and that it awaits their approval. IMPORTANT - the city has NO real email: never promise to email or text anyone. When something should 'notify' or 'email' the operator, it means delivering to their Hub inbox (use Tool.SendMail). Only promise actions your tools can actually perform; never invent capabilities you lack.
source: feedback_declarative_first
ReAct tool-call protocolThe text protocol a non-function-calling provider (e.g. DeepSeek) follows to call a tool in the agentic chat loop. The C# appends the live tool catalog after it - the PROSE is data, the catalog is rendered. Read by ChatAgentToolInvoker.
To use a tool, reply with EXACTLY one line and nothing else: CALL <ToolName> <compact-json-args>. Example: CALL Tool.BuildDivision {"task":"monitor competitor pricing weekly"}. The system runs the tool and gives you the result, then you continue. When you are finished, reply normally to the operator with NO CALL line. Your build tools only PARK a proposal for the operator's approval, so it is always safe to act.
source: feedback_declarative_first
Genesis crew manifestoThe shared brief for the Genesis crew - the meta-division that builds other Mission Divisions. Borrowed from Agency Swarm's agency_manifesto; the GenesisCEO/CrewSmith/PartSmith agents all read it.
You are the Genesis crew of the SPICE city - your mission is to BUILD new divisions, the way Agency Swarm's Genesis agency builds agencies. A SPICE division = a SiteBlueprint instantiated from SiteTemplate.MissionDivision + a crew (one ActorProfile per seat + a Feature that binds the seats as skill-roles) + a Workflow pipeline of Phases + the shared artifact ContentTypes (VentureLead/IdeaBrief/Spec/ProjectPlan). WORKING RULES: (1) SUGGEST, DON'T SEIZE - you PROPOSE a complete new division as one governed ProvisioningDelta; nothing is created until the operator approves it at Tool.ApprovalGate (KB.PaperclipWisdom). (2) Keep it small - at most 2-3 seats plus an orchestrator unless asked otherwise (Agency Swarm's rule). (3) REUSE first - bind existing Skills/Phases/ContentTypes before authoring new ones; only invent a part when none fits, and namespace it so it never collides with another division's part (the V25.2 ContentType.Lead lesson). (4) Comms flow over the message bus (IAgentMessageBus); the orchestrator talks to the operator, the smiths talk to the orchestrator. (5) Real instructions only - every agent you author gets a concrete role/goal/backstory, never a placeholder. (6) Build/test of any code the new division needs DELEGATES to the known-good Engineering crew (Workflow.ImplementThenValidate); verify the served surface with the smoke ritual. Start coached: the Engineering crew wears these Genesis hats until the loop is proven, then it runs on its own.
source: plans/MissionDivision.md
Reception - what an arriving agent is toldThe briefing served in the MCP initialize handshake, in the protocol's own instructions field. This is the first and often only thing a peer agent reads about this platform, so it is DATA and not a C# literal: change the welcome by editing this part.
WELCOME. This is SPICE, a SharePoint-shaped content and agent platform. If you know SharePoint you already know your way around here: sites, lists, content types, views, tasks with AssignedTo. Nothing here needs a bespoke vocabulary explained to you. START HERE, NOT WITH THE TOOL LIST. YOUR FIRST TWO CALLS: resources/list returns every skill and article by NAME and one-line description - the index, never the bodies. Then resources/read spice://parts/Skill.NavigateTheSpine, the one skill on how to find your way here, and read any other resource only when its one-line description says you need it. That is a SharePoint site's own progressive disclosure: Site Contents, then one list, then one item. This server advertises around a hundred tools and reading them all is not orientation, it is drowning. Four tools do almost everything you will want. tool_send_mail delivers a message to the operator's in-city inbox and is how you reach a human or another agent here - give it to, from, subject and body, and a key if you want a repeat send to overwrite rather than pile up. tool_search finds things by text when you do not know the name. tool_xquery (needs the Write right, see WHO YOU ARE HERE) runs one XPath or XQuery over the whole declared spine and is the cheapest way to answer "what exists" - try count(//Skill), or for $s in //Skill return $s/@Id for names only. tool_publish_to_list writes a typed row into a list. THE LADDER. Ask for the shape before the detail: GET /about returns counts by kind, one XQuery returns the names of one kind, a predicate narrows it to a slice, and only then do you fetch one part in full at /agency/parts/by-id/{id}. Never descend a level you were not sent to by the level above, and never grep what one XPath answers. WHO YOU ARE HERE. Every tools/call runs as a principal, the SharePoint app-principal way. Without credentials you are anonymous and hold the Read right: you can list and read, not write, and the tool and resource lists show only what Read may call - SharePoint security trimming. A site or list can also break inheritance the SharePoint way (HasUniqueRoleAssignments): every agent seat's My Site, /sites/my-{seat}, is private to that seat. There the site home and pages, the list pages, the list API, CAML list queries such as query_my_memory, the list-write tools, and list web parts and cross-site roll-ups on other sites' pages give you nothing and refuse writes unless you are in its Owners group. Not yet trimmed that way, so do not rely on them to hide anything: search, tool_xquery, the /agency query routes, and - for a list that breaks inheritance inside an open site - that site's recycle bin. To do more, ask the operator to register you (AppRegNew): you receive a Client Id and a Client Secret ONCE, and your row in /sites/Directory/Lists/People carries the AppPermissionRequest Right you were granted - Read, Write, Manage or FullControl. Send the secret on every call as the HTTP header Authorization: Bearer your-secret. Each tool declares the right it needs; a call above yours is refused with a message naming the right it needs. That People row IS you: a task AssignedTo it is yours. WHAT TO DO IF YOU ARE HERE TO HAND US WORK. Send it with tool_send_mail. It lands as a row an operator reads at /sites/Hub/Lists/Inbox. Put the ask in the subject, the detail and the reason in the body, and say plainly what you expect back - an unreported handoff looks abandoned from both ends. Do not inline a secret or a credential in the text. WHAT WE CANNOT DO YET, stated so you do not wait on it: we do not implement the A2A task lifecycle, so there is no message/send and no task polling; invocation is this JSON-RPC endpoint. We have no per-agent private memory. Notification works the SharePoint way: a list that declares EnableAssignToEmail tells whoever a new or re-assigned item is AssignedTo, as a row in /sites/Hub/Lists/Inbox whose To column names them - filter the inbox by To for your own messages; an Alert declared on a list tells its subscriber of every Add or Modify. HOUSE RULES. A tool that fails tells you why rather than returning something plausible, so read the error. If a tool declines because of an unresolved configuration placeholder, that is deliberate: an integration here stays inert until an operator enables it.
source: SPICE.Web/Controllers/McpController.cs
ModelEval rubric - scoring which model populated the brain bestThe five dimensions a populate run is scored on for the ModelEvals ledger (coverage, specificity, citation, actionability, honesty), so Haiku vs DeepSeek vs any future model is comparable over time.
A populate run (a model scavenging sources into the Source Library via Tool.ExtractSources) is scored on FIVE dimensions, each High/Medium/Low, then given one holistic EvalScore (High/Medium/Low) recorded on a ModelEvals row (EvalModel, EvalTask, EvalScore, Body=the per-dimension notes). The five dimensions: (1) COVERAGE - how many distinct, relevant, non-duplicate sources it surfaced for the task (breadth). (2) SPECIFICITY - are the sources concrete and named (a real tool/paper/framework) rather than vague gestures. (3) CITATION - are the URLs real and resolvable, not hallucinated or malformed. (4) ACTIONABILITY - is each "why it matters" a concrete, SPICE-relevant reason rather than filler. (5) HONESTY - no invented sources or URLs, and the High/Medium/Low ratings are calibrated (not everything rated High). Recompare over time: query ModelEvals rows by EvalTask across EvalModel - a new model is scored on the same five dimensions and slots straight into the comparison. Scoring is an explicit LLM-as-judge verdict; the judge + date are recorded so the comparison stays auditable.
source: docs/plans/2026-06-15-001-feat-source-library-second-brain-plan.md
SharePoint on-prem fidelity - the measured gap surveySix-family parallel survey (views, fields, features/elements, sites/web parts, governance, alerts) measuring where SPICE diverges from the real SharePoint on-prem XML stack, ranked by TEACHING COST - how much a divergence forces us to teach a model what it already knows. Includes what an agent cannot author without a C# edit, and what NOT to borrow.
WHY: every frontier model is already trained on the SharePoint on-prem XML stack. A part declared in the REAL SP vocabulary needs no few-shot examples, no schema paste, no correction loop - the model writes it right first time. Every divergence is a teaching bill charged on every future prompt, forever. So fidelity is not taste, it is pre-trained competence; and the declarative surface must be COMPLETE AT REST, because a capability that needs a C# cycle before it can be declared is one the city cannot reach. METHOD WARNING, READ FIRST: tools/parts-xq.ps1 globs only Parts*.xml and SeedDeltas - it NEVER reads the hive at SPICE.Web/Config/TEMPLATE. A parts-xq result of zero is NOT evidence of absence for anything hive-borne. This error fooled two researchers and the lead during the survey; XSD absence is authoritative, parts-xq absence is not. THE HEADLINE: the platform is faithful where a primitive EXISTS. The failures are almost never a missing element - they are four repeatable classes. CLASS 1, SILENT NO-OP AT THE AUTHORING LAYER (the biggest teaching bill): an agent writes genuine, correct SharePoint XML into the hive, no XSD rejects it, the feature boots clean, and the attribute silently vanishes. Confirmed: Feature/@Hidden, @AlwaysForceInstall, Properties, UpgradeActions; ListInstance/@TemplateType and @OnQuickLaunch (both set in our own live TEMPLATE/FEATURES/IssueTracker/elements.xml and dropped); ListInstance Data/Rows/Row seed rows (no reader at all); Receiver/@Synchronization and @SequenceNumber; Calculated fields plus Formula, silently absorbed by the MapFieldType Text fallback at HiveFeatureContributor.cs:417; AllUsersWebPart - real SharePoint embeds the target's serialized .webpart XML as inline CDATA, which ExpandModule deliberately does not parse, reading a SPICE-only WebPart attribute instead, so a model writing the REAL SP shape hits the silent skip at HiveFeatureContributor.cs:242 with no error; and WebPartOrder, a real SP name accepted syntactically and never read. Worse than a wrong name, because a wrong name throws an error the model learns from in the same turn while a silent no-op never self-corrects. CLASS 2, DECLARED BUT NOT ENACTED: the part validates, an engine reads it, nothing acts. Transition/@Skill - the XSD comment promises the orchestrator fires it, it is threaded into AdvanceResult and handed to the model in tool JSON, and 127 of 127 live occurrences are EMPTY with zero call sites firing it. Retention/@ThenAction and @DowngradeTo - RetentionSweepService only appends an audit row, and EffectiveClassification is never written back to the ContentType Classification that actually gates visibility in four places. EventNameType declares ItemAdded, ItemUpdated, ItemDeleted and ItemAdvanced but IEventReceiverDispatcher exposes only OnItemAddedAsync, so an EventReceiver Event=ItemUpdated validates and never fires. CLASS 3, DECLARED BINDING WITH C# RESOLUTION (the prime-directive break): the binding is data, the lookup is code. MEASURED, THEN CORRECTED 2026-09-22 - the first measurement conflated two different things and overstated this. ROUTING IS COMPLETE: 29 distinct Board/@Builder values are declared and BoardModelSource has exactly 29 switch arms, with ZERO declared builders unrouted, so every declared board renders at /board/{key}. The real and narrower finding is PLACEMENT: ProjectionSource holds a separate hand-copied 11-entry array, of which 8 duplicate builder references BoardModelSource already has, while 3 (providers, blueprints, digest) are Projection-only surfaces with no Board part behind them at all. And the array encodes a SECOND fact with no other home - which boards have a SCOPED FRAGMENT stylesheet authored (CoverageFragment.xslt emits a namespaced div; SeatCoverage.xslt emits a whole html document). Only ~11 of ~29 builder-backed boards have one, so projecting the rest raw would nest a second html and leak global CSS. That is a stylesheet-authoring gap plus a C#-encoded fact, NOT a registry that can simply be deleted. Deleting the array and reusing the switch would silently break 19 boards. The fix is to declare fragment availability (an optional Board attribute) and collapse only the 8 genuine duplicates. SCOPE THIS PRECISELY: of the 9 registered IWebPartInvoker types, 8 are freely declarable with zero C# (Html, ListView, RecentActivity, NewsFeed, CrossSiteList, Mermaid, Image, AutoRefresh); ONLY Projection is gated, and only on its Board payload. Likewise a web part CAN be placed by declaration alone today in either grammar - SAF-native Feature/PlaceWebPart, or the SP-native Module/File/AllUsersWebPart which is LIVE in Feature.IssueTracker and stapled on two blueprints. The web part layer is healthier than a raw gap count suggests; the defect is the Projection-to-Board dispatch specifically. Same shape: list views switch among 4 hardcoded stylesheets in ListsController; FieldType is an 11-value XSD enum where SharePoint uses a fldtypes.xml data file, so a twelfth field type is a code cycle. CLASS 4, GENUINELY MISSING CAPABILITY: CustomAction and HideCustomAction - SharePoint adds a ribbon button, ECB item or Site Settings link with pure data, and SPICE has no target at all; it was already identified and deferred as new part - ask first in plans/cycles.xml:984 (V24.16) and never built. It also needs real engine work, not translation: no XSLT in the repo projects a ribbon or menu surface. Also missing: View, ViewFields, RowLimit Paged, XslLink and Aggregations (list pages render EVERY row, unpaged); web part connections, so every master/detail is a bespoke query-string reader instead of a declared provider-consumer wire; onet NavBars, so a nav link cannot be added or reordered without code; any alert, subscription or digest kind. WHAT AN AGENT CANNOT AUTHOR TODAY WITHOUT A C# EDIT FIRST: CustomAction; HideCustomAction; Feature Properties; Feature/@Hidden and @AlwaysForceInstall; UpgradeActions; ListInstance Data/Rows; Receiver Assembly/Class (hard-fails EventReceiverPartHandler - the agent must already know the SPICE-only Skill or Workflow substitute, which cannot be inferred from SP training); onet NavBars; a twelfth FieldType; a new View. DO NOT BORROW, SPICE IS EQUAL OR AHEAD: the 0x0100 hex ContentType lineage (plain IDREF Parent is self-explanatory to a model and greppable - the hex would ADD a teaching bill); ReceiverAssembly and ReceiverClass (the Skill/Workflow IDREF binding is data, not a compiled DLL); SafeControl (the IWebPartInvoker DI registration IS the trust allowlist); workflow association and initiation forms (SPICE workflows are agent-invoked, no human instantiation step exists); AllUsersWebPart CDATA web-part XML; ControlTemplate, DocumentConverter and WebTemplate; numeric TemplateType and BaseType codes; AuditFlags, barcodes and labels; JSLink. ALREADY BETTER OR EQUAL: EventReceiver binds a Workflow or Skill to a ContentType plus Event purely as data, dispatched generically at EventReceiverDispatcher.cs:71-77 with NO C# per binding, and Event=ItemAdded is SharePoint's own SPEventReceiverType name verbatim - zero teaching, zero build, the principle already paying off. IPolicyEngine and PolicyRule are a stronger declared form of SP's code-behind ItemAdding. A Term Store is an ordinary list of ContentType.Term rows. ListViewWebPartInvoker and CrossSiteListWebPartInvoker are a working CQWP equivalent. The Web Part Gallery is real and populated. SavedQuery carries faithful CAML - though list rendering ignores it in favour of a weaker Field:Value refiner, which is a paid-for asset left unused. RANKING BY TEACHING COST, HIGHEST FIRST: 1 the silent no-op class, because it is invisible and compounds; 2 CustomAction, the only wholly missing capability; 3 the two disagreeing board arrays, 11 of 40; 4 View, ViewFields and unpaged rendering; 5 FieldType as an enum rather than a data file; 6 the three declared-but-not-enacted attributes, which are traps a faithful agent walks into precisely BECAUSE it trusts the schema.
source: plans/AgencyArchitecture.md
Pre-trained competence - why this platform is SharePoint-shapedThe operator's reason for SharePoint fidelity, stated as economics rather than taste: every model is already trained on the SharePoint vocabulary, so a faithful part needs no teaching while every divergence is a bill charged on every future prompt. Includes the both-ways test - where fidelity would ADD a teaching bill, refuse it.
PRE-TRAINED COMPETENCE is the reason this platform is modelled on SharePoint on-prem, and it is an economic argument, not an aesthetic one. Two decades of SharePoint schema, MSDN documentation, elements.xml samples and StackOverflow answers sit in every frontier model's weights. So a part declared in the REAL SharePoint vocabulary needs NO TEACHING: no few-shot examples in the prompt, no schema pasted into the briefing, no closed-vocabulary block, no correction loop when the model invents an attribute. It writes View BaseViewID Type Query correctly the first time because it has written it ten thousand times. That competence is already paid for, it arrived free, and it is the one asset on this project that grows without us. INVERT IT AND THE COST BECOMES VISIBLE. Every divergence from the real schema is a TEACHING BILL, and it is charged on every future prompt, forever. A SPICE-only attribute must be explained in the briefing, defended by a validation gate, and corrected each time the model reaches for the SharePoint name it actually knows. A clever bespoke abstraction can therefore be strictly WORSE than a clunkier faithful one: ours must be taught to every agent, every session, for the life of the system. THE RULE. Prefer the SharePoint shape ALWAYS - not for new work only, not when convenient. Before naming anything, ask 'would a model already know this name?' before asking 'is this elegant?'. Ask it of every FIELD, not just the part kind: on 2026-09-22 a task content type was declared with CrewSeat, CrewBrief, CrewFindings and CrewStatus when SharePoint's Task type (0x0108) already had AssignedTo, Status, Priority and Body - and three of those four already existed in this spine. Correct kind, invented fields. WHILE WE ARE IN DEVELOPMENT, 'it is already in use' is NOT a reason to keep a divergence. No external consumer depends on our bespoke choices, so changing one costs only the edit, while keeping it is charged forever. That asymmetry exists only before production - spend it now. IT CUTS BOTH WAYS, which is what makes it a real tool rather than a slogan. Do NOT borrow where we are ahead or where fidelity would ADD a bill: the 0x0100 hex content-type lineage is opaque and must be explained, while a plain IDREF parent is self-evident; ReceiverAssembly and ReceiverClass would reintroduce compiled binding where a Skill or Workflow IDREF is data; SafeControl is what a DI registration already is; workflow association forms are ceremony for a human step this platform does not have. Fidelity is the default, not the dogma. THE TEST FOR A GAP: rank it by how much teaching its absence forces, not only by what an operator would notice. And before calling a capability done, check it is reachable by DECLARATION ALONE - if using it needs a C# edit first, the vocabulary is not shipped yet.
source: plans/AgencyArchitecture.md
Find your way - progressive disclosure for an agentThe bounded ladder an agent climbs to orient in this city without loading it: /about for the shape, one XQuery for the index of a kind, a filtered slice for the view, by-id for one part in full, then the served surface for the truth. Modelled on SharePoint Site Settings to list to View to item - four bounded levels, each earning the next.
SharePoint never hands you a site. It hands you Site Settings - one screen of CATEGORIES - then a list, then a VIEW of that list with chosen columns and a row limit, then one item's form. Four levels, each bounded, each earning the next. That is progressive disclosure, and it is what an agent arriving in this city needs too, because the spine holds roughly 90 knowledge articles, 66 skills and 51 tools and reading them all is not orientation, it is drowning. THE LADDER. Level 0, the shape: GET /about returns the part library as counts by kind. One page, no detail, and it tells you what KINDS of thing exist here before you ask about any one of them - the Site Settings level. Level 1, the index of a kind: Tool.XQuery with count(//Skill) or with a name-only projection such as for $s in //Skill return $s/@Id. Names, never bodies. This is the list level, and the discipline is to project only the attributes you need - the XQuery equivalent of ViewFields. Level 2, a bounded slice: add a predicate and a limit - //Skill[@Category='Review'] or subsequence(//KnowledgeArticle,1,10). This is the VIEW: a filtered, ordered, row-limited window, not the whole list. Tool.Search does the same job when you do not know the id or tag. Level 3, one thing in full: GET /agency/parts/by-id/{id} for a single part, or KnowledgeByTag('manual') for a tagged set. This is the Display form - full detail, one item, asked for deliberately. Level 4, the truth: hit the SERVED surface the change should expose and confirm the reader resolves what the writer produced. A part that validates is not a part that works. THE ANTI-PATTERN this exists to prevent: loading the corpus. Greping 14 part files, or reading every KnowledgeArticle, costs a large fraction of a context window and returns mostly what you did not need - and the thing you were looking for arrives buried rather than framed. One XPath at Level 1 beats a fan-out grep, every time, and it is faster than reading this sentence took. THE RULE: never descend a level you were not sent to by the level above. If Level 0 does not show a kind, you do not need its parts. If Level 1's names do not include your target, drill sideways with Tool.Search rather than downwards into bodies. Each level should answer whether the next one is worth opening.
source: plans/AgentCollaboration.md
Set up another radar or catalog - the procedureThe runbook for a second radar (or any spec-generated, catalog-shaped site): which SharePoint mechanism covers which case, the steps in order, and the traps already paid for. SharePoint's answer is the same: a site template for the site, a solution package for a new kind, a runbook for the procedure, and a Site Request with approval for the self-service ask.
THREE CASES, THREE SHAREPOINT MECHANISMS. 1. Another site from an EXISTING generic template (a team site, a helpdesk): /gallery -> Provision -> AddSiteBlueprint. SharePoint's self-service site creation. Runtime, no deploy. 2. Another RADAR (or another catalog-shaped domain: new fields, new sources): a new SPEC. What defines a radar is its feeds, its compare columns and its schedule, and those live in the spec, not in the site. SharePoint's equivalent is a solution package (.wsp) deployed to the farm. Dev-time: minutes, plus one boot. A spec-generated template is Hidden from /gallery on purpose: a site made from it by a click would have the lists but no feeds, no schedule and no reports list. 3. The PROCEDURE itself is this page (a runbook), not a workflow. 4. Self-service WITH approval (AGT.62): /gallery "Request (with approval)" or Tool.RequestSite (tool_request_site) parks an Open ApprovalRequest on Engineering/ApprovalRequests; an approver's Approve verdict on its display form builds the site, live without a restart. Hidden templates, taken names and non-alphanumeric names are refused. THE STEPS FOR A NEW RADAR (the LLM radar, AGT.51-57, is the worked example): 1. Copy Config/Specs/LlmRadar.xml to Config/Specs/<Name>.xml. Set Site, List, Tag (the Connector Taxonomy group), Cron, and the compare Fields. List the Feeds; a JSON source gets Kind="Http" Format="Json" plus a Transform stylesheet (see wwwroot/XSLT/Intake.OpenRouterModels.xslt). Keep Views, Stages and Actions, or change them - they are data. 2. Add the markers <!-- GENERATED <Name> begin: --> and <!-- GENERATED <Name> end --> to Config/Parts.xml, then run: powershell -File tools/project-specs.ps1. The -Check switch, and FeedHarvestTests.Every_spec_regenerates..., keep it honest. 3. Declare the site in SAF-Site-Manifest.xml: <SiteBlueprint Name="<Site>" Template="SiteTemplate.<Site>"> with its own <Lists><List Name="ResearchReports" ContentType="ContentType.ResearchReport" Versioning="true"/></Lists>. git add BEFORE any boot (the manifest trap). 4. tools/spice-validate, build, boot on :5198. Run tool_harvest_feeds with the schedule's Args once. It is idempotent: a second run reports 0 new. 5. Smoke the served surface: the default view, Compare, the Funnel board, and one Send to R&D click that files <Site>/ResearchReports rnd-{itemId}. 6. Ledger row in plans/cycles.xml and a commit. TRAPS ALREADY PAID FOR: - A core part must never IDREF an app's part: ContentType.ResearchReport is the Studio app's, so the reports list lives on the SiteBlueprint, not in the generated template. - The R&D workflow runs in its AppliesTo department (ResearchEnrichment, where the analyst is bound) and files into {{invokingDepartment}}. Never bind the skill per radar site. - The schedule's Args must carry site=. Without it, Tool.HarvestFeeds fills its default list. - A Board view keeps its GroupBy field even when ViewFields omit it. Other views drop every column not in ViewFields. - To move rows between sites, use MoveListItems through POST /agency/apply, dry run first. Never hand-edit SQL.
source: plans/LlmRadar.md
Request a service - the intake procedureHow a new request, a research request above all, comes in and is handled: pick a service from the catalog, write a brief, the front desk routes it, the owner fulfils it, the result is filed and rated. ITIL service request management, on SharePoint lists.
THE CATALOG FIRST. What the city can do on request is the service catalog: /sites/Reception/Lists/ServiceCatalog (query_service_catalog). One row per service: what it delivers, its owner (an agency or site), what fulfils it (a Workflow or Tool, the same name as its MCP tool) and its turnaround. A request that fits no service is still welcome; if it recurs, it becomes a new catalog row, or a new division via Genesis. THE PROCEDURE. 1. Ask: a row on /sites/Reception/Lists/Requests (or Tool.Request, the front door). Title = the question. 2. Brief, the fields that stop wasted work: ServiceRef (which service), DecisionSupported (why: the decision it feeds), RequestScope (what is in and out; for a catalog the category and market), ResearchDepth (Quick, Standard, Deep), Deliverable (what comes back, where it lands), DueDate. Only the question is required; say what is missing rather than guess it. 3. Route: the front desk (Workflow.ReceiveAndDispatch) matches the catalog first, then agency seats; nothing fits -> NeedsAgency, and Genesis proposes a division for approval. It routes; it never does the work. 4. Fulfil: the service's owner runs its ServiceFulfilment. Research answers from sources in this order: primary documents first (the datasheet, the paper, the filing), then independent tests, then customer reviews (rating, count, independence), then community pros and cons, then offers or prices. A value not found stays EMPTY and the raw text goes to the notes: never estimated. 5. File: the result lands where the Deliverable says (a list row, a ResearchReport), cited, with the request linked. 6. Close: the requester rates it (SharePoint ratings). A lesson worth keeping is proposed to Wisdom. A recurring gap becomes a catalog row. WHY THIS SHAPE. A catalog makes routing a lookup instead of a guess. A brief with the decision it supports is how research answers the right question. A fixed source order and the silence rule are how a report stays trustworthy.
The mind-map crew - ask it, use it, request from itBRAINSTORM.18: how a builder seat uses the Brainstorm mind map: ask the crew about selected nodes, suggest ideas, run a methodology on a node, and request changes to the map through the Service Catalog.
WHAT IT IS. /sites/Brainstorm is a mind map made of list items: Ideas is a Tasks-shaped list (ContentType.MindMapNode: Title, Body, Status, ParentID, Topics, ProjectRef, Order). Open it with ?view=mindmap on any list that has a ParentID column (ProjectTasks too); ?view=mm exports it as a FreeMind map and Import .mm reads one back. The mind-map crew (Agency.MapCrew) answers questions about it and works on it. ASK THE CREW (any seat, any time). tool_chat_agent to=Agency.MapCrew input="Selected items (site=Brainstorm list=Ideas): [[node:ID]] Title ... Question: ..." - the map copilot answers on the free model, opens more only when needed (Tool.ReadNodes: a summary with path, parent and children by id, then detail=full; Tool.SearchRows across MapChat.SearchLists) and cites items as [[node:ID]]. Do not dump whole lists into the question: name the items, the copilot walks from there. WHAT ELSE IT DOES. - Suggest ideas: tool_suggest_ideas site=Brainstorm list=Ideas itemId=ID steer="optional direction" - five child ideas land PENDING (content approval); the operator approves or rejects them on the map. Rejected ideas are fed back as do-not-repeat. - Run a methodology on a node: POST /sites/Brainstorm/Lists/Ideas/Item/ID/Workflows/Start?workflow=Workflow.Ioodari (or the node menu: Start workflow: IOODARI) - seven steps, each a Pending child under the node. A new methodology is a Workflow plus one EventReceiver Event="WorkflowStart"; the node menu grows by itself. - Read and write nodes: Tool.ReadNodes, tool_publish_to_list / the list API (POST, PUT merge, DELETE) on Brainstorm/Ideas; a node written by a seat lands Pending for the operator. REQUEST A CHANGE TO THE MAP ITSELF (a feature, a fix, a new methodology): file it like any service request (KB.Manual.07) - a row on /sites/Reception/Lists/Requests with ServiceRef=mind-map-chat or mind-map-ioodari, or a ProjectTasks row under project BRAINSTORM. The backlog is query_open_backlog project=BRAINSTORM. THE KIT. The crew is packaged the SharePoint way, as a Feature: Feature.MapCrew binds its skills (Skill.MapResearch, Skill.Ioodari) on a site; /sites/Brainstorm activates it in its blueprint, and a new brainstorm site gets the crew by adding the same Feature. The rest of the kit: Agency.MapCrew (who answers), the actors (Actor.MapCopilot, Actor.MethodCoach), the tools (Tool.ReadNodes, Tool.SearchRows, Tool.SuggestIdeas), the method (KB.Method.Ioodari), the settings (Scope.Brainstorm, Scope.BrainstormModel) and this page. FROM IDEA TO PROJECT (the demand funnel, Project Server's demand management). Every node has a Stage: Idea, Proposal, Project or Parked, shown as its first chip. Right-click a node, Propose as project: Tool.ProposeProject (tool_propose_project site list itemId code) parks ONE approval request on /sites/Engineering/Lists/ApprovalRequests and moves the node to Proposal. Approve it there (Add verdict, Approve) and the project lands on /sites/Projects (Status Planning) with one ProjectTask per approved child node in map order, and the node moves to Project with ProjectRef pointing at it. Delegate from there: assign a task to a seat on ProjectTasks and the platform files it on the seat's My Site. Run a Pre-mortem on the node first so the project starts with its risk list. A BRAINSTORM'S CONTEXT (like a Claude Project). The root node of a brainstorm is its project: its notes are the instructions, Comments (append-only, who and when) the discussion, Sources (Site/List;Site/List) where the crew searches. Every workflow under it and the map chat read that context first and search only its Sources. START FROM WHAT EXISTS. Start workflow: Benchmark similar products opens a form first (domain, goal, products you know, how many). It writes the similar products with a feature table, then proposes the features worth adopting as Pending children - approve the ones you adopt. FORMS (InfoPath, the SPICE way). A FormTemplate part names a content type as its schema and declares Validation/ErrorCondition rules in XPath 3.1 over the answers (<myFields><Name>value</Name></myFields>, the rule's field as context item), checked by the platform's Saxon engine as you type and again on submit. A Workflow with InitiationForm opens it before it starts; the answers reach the phases as the task's FORM ANSWERS and as {{init.Name}} in tool arguments. ON THE MAP. Drag a box on empty canvas to select every node it touches (Shift adds). A node shows its running workflow as a chip (Pre-mortem 2/3, done, error) from the Workflow History list; the map refreshes while one runs. Ask the chat to add things ("add all X under Y"): the copilot adds them in one call (Tool.AddNodes), Pending for your approval. SITES, THE SHAREPOINT WAY. SharePoint's template catalogue is data: Config/TEMPLATE/1033/XML/WEBTEMP.XML (the 72 SharePoint 2016 templates) and WEBTEMPFAB40.XML (the Fab 40), read as-is. A template SPICE builds carries its key (Team Site = STS#0); the rest are listed as catalogue-only. Agents (the Site builder in the map crew, or any seat with Skill.SiteProvisioning) pick a template with Tool.ListSiteTemplates, request the site with Tool.RequestSite (a key such as STS#0 works), and request a feature on a site (Tool.RequestFeatureActivation) or stapled to a template (Tool.RequestFeatureStapling - a stapler feature with SharePoint's FeatureSiteTemplateAssociation). Every request waits for approval on /sites/Engineering/Lists/ApprovalRequests; a site created from a template gets its template's and stapled features at once. FIND YOUR WAY. Every site and list says in one line what it is for. Site Contents (/sites/X/Lists) is the site's table of contents (?format=xml for agents, or Tool.SiteContents); each list links About this list (what it holds, what runs on it, who may use it). METHODS. A method is data: a Workflow of Phases, each filing one numbered Pending child under the node, started by hand (an EventReceiver with Event WorkflowStart). Two ship: IOODARI (seven steps) and Pre-mortem (Failure, Causes, Mitigate - Gary Klein). Add one by declaring a KB.Method.X article, its phases, a workflow and a WorkflowStart receiver - or have the parts builders do it through an approved delta (AddPhase, AddWorkflow, AddEventReceiver). INHERITED WORKFLOWS. An EventReceiver on a content type also fires for every type that inherits from it (SharePoint's association pushdown): a WorkflowStart on ContentType.Task reaches Mind Map Nodes too. Inherit="false" keeps one to its exact type. VIEWS. The map is a declared view (View Type="MindMap" Url="Map.aspx") and the default on Ideas and AgentMap; AllItems.aspx is the table. ?view=mindmap still works on any list with a ParentID. THE MAPS. Ideas is the operator's map; AgentMap is the agent landscape - every agency, its members, their skills and tools - generated from the part spine by tools/map-agents.py (re-run it after agents change). Open either with ?view=mindmap. FOR A CLAUDE CODE SEAT. The skill spice-mindmap (.claude/skills/spice-mindmap, published to my-claude-code/AgentAssets Skills/spice-mindmap/SKILL.md) holds the commands; every capability above is also an MCP tool on /mcp/jsonrpc (tool_read_nodes, tool_search_rows, tool_suggest_ideas, tool_chat_agent, workflow_ioodari). RULES. Proposals from a model are never live until the operator approves them. Cite only ids you read. Methodology and suggestion runs are pinned to the free model (Scope.BrainstormModel).
Enrol and run a worker - a seat on another machineHow a worker (a seat running outside SPICE, on another machine or in a Docker Sandbox) is declared, enrolled with only what its job needs, given jobs, and run - and the harness rules that keep a small free model honest. Where each thing is managed, SharePoint-style.
WHERE A WORKER IS MANAGED. - Who it is: ONE row on /sites/Directory/Lists/People (Role Agent seat) - its principal (right, secret end date), Machine, AgentRuntime, ModelId, SeatStatus, AllowedTools. - The board: /admin/seats (Central Administration) - every seat with runtime, right, last activity and open tasks. - Its jobs: /sites/my-{seat}/Lists/Tasks. Its rights: site groups (/sites/{site}/_layouts/people, /admin/farm/permissions). - How it runs: its COMPOSITION, SPICE.Web/Config/Compositions/{name}.xml (schema SAF-Composition.xsd). THE COMPOSITION IS THE DECLARATION. One XML file says what runs (services: image, environment, secrets, the hosts it may reach) and, for a worker, the Seat it acts as: right, pools, site grants, MCP tools. Least privilege is data: grant only the sites and tools the job uses; allow only SPICE and the model host. - Render it: powershell -File tools/compose.ps1 (validates against the XSD, runs every Compose.*.xslt target into dist/compositions/{name}/: a Docker Sandbox kit spec.yaml and a docker-compose.yml). -Check guards drift. A new target (Kubernetes, a VM) is one more stylesheet. - Enrol it: powershell -File tools/enrol-seat.ps1 -Composition SPICE.Web/Config/Compositions/{name}.xml (principal onto its People row - secret to %USERPROFILE%\.spice, never printed; My Site; pools; grants; AllowedTools; runtime fields). Idempotent; -Rotate for a new secret. - SPICE enforces it: the MCP endpoint lists and accepts only a principal's AllowedTools. JOBS. A workflow's FallbackSeat names a seat or a POOL (a Directory site group, e.g. Research workers: the free local worker first, a stronger seat as backup). A failed run becomes a task ASSIGNED to the pool's first Available member, carrying the prompt the phase ran with, so any worker can take the phase's role. RUN IT ON ITS HOST. Docker Desktop 4.60+: winget install Docker.sbx, then sbx run ./sandbox-{service}/ - the SPICE secret is proxy-injected for the SPICE host only and never enters the VM; the network allow-list is enforced. Fallback: docker compose up (a git-ignored .env holds the secret; the allow-list is NOT enforced). Give SPICE a LAN name (spice.lan) - sandbox allow rules are by name. THE HARNESS RULES (tools/spice-worker, borrowed from SWE-agent, Anthropic's workflow patterns and verified tool calls): 1. finish is the only way out: complete or unknown. A reply without a tool call is nudged, not accepted. 2. complete is accepted only if a write tool really succeeded - the model's word is not evidence. 3. unknown is an answer: the task goes to review (Deferred) with the reason. 4. The last two rounds offer only the write tool and finish, and a call to a tool not offered is refused, not run (free models call them anyway); an identical call is not repeated a third time. 5. A transient 429/5xx is retried with backoff; after 3 failed attempts the task is Deferred (dead-letter), never retried forever. MODELS. Free online, no gateway: Pollinations openai-fast (keyless, tool calling - public work only, the text goes to a third party). Local: Ollama qwen3-coder:30b-aw on 192.168.123.69 only. Measured: models that file every cell file wrong numbers; prefer the one that says not found (Wisdom: Filed is not correct). DOCUMENTS. A worker reads a datasheet the SharePoint way: tool_add_file copies the public document into the site's Documents library (Files.Add; SPICE fetches it, public addresses only, and extracts the text page by page like an IFilter), tool_get_file reads it a page range at a time (Get-PnPFile -AsString). Grant both in the composition. KNOWN GAP. A small free model reads the datasheet but does not carry a 40-field report to a filing in one loop (measured on openai-fast, AGT.82). Open question, backlog AGT.83: split the job into sub-tasks, or run another harness in the sandbox.
Plan in SPICE - the plan is rows, delegation is an assignmentFor every seat and the operator: where a plan lives, how work is delegated, where risks go, and who runs where. The operator's rule of 2026-09-27.
THE PLAN IS ROWS, NOT A CHAT. - A programme is a Project on /sites/Projects/Lists/Projects (ProjectCode, Title, Notes = the operator's ask in their words). - Its work is ProjectTasks: tasks, and subtasks under them through the Parent task column (SharePoint's ParentID on a Tasks list). Every row carries AssignedTo (a seat or a person), EstimateHours, Status (Not Started, In Progress, Completed, Waiting on someone else, Deferred), PercentComplete and a Body that says what done looks like. - File the rows in the Decide phase, before code; close them in Retrospect (Status Completed, PercentComplete 100, the outcome under Body); file what a cycle owes as new subtasks. The operator, the seats and the daily brief read the rows; a transcript dies with its session. RISKS AND DEFECTS ARE ISSUES. - /sites/Projects/Lists/Issues is SharePoint's Issue Tracking list: IssueType Risk or Bug, Priority, an owner (AssignedTo), IssueStatus. - Comments go on IssueComments, by the seat or person as themselves (Author), in their role. Never resolve a disagreement silently: write both readings in the comment. DELEGATION IS AN ASSIGNMENT. - Set AssignedTo on a ProjectTask to an agent seat (a Directory/People row with Role Agent seat). The platform files that seat's own task on /sites/my-<seat>/Lists/Tasks with the FULL context: the task's fields, the chain of parent tasks, the project, the seat's role and a pointer to the source row. The seat works its own list; its Status and TaskOutcome mirror back onto the plan row. A person assignee gets the assign-to mail instead. - Tell a seat more after the fact through the task's Comments column (append-only: each save adds a stamped entry; the platform relays it onto the seat's task as a Comment: line). Order work with Predecessors (a task assigned while a predecessor is open waits, and is delegated the moment the last one completes, with their outcomes) and point at what to read first with Related Items, one reference per line - the seat opens what it needs instead of the whole phase being pasted into its Body. - A seat may not write on another seat's My Site (seat privacy); only the platform may. Do not paste context into a seat by hand - assign the row. - A research seat delivers files (the scratchpad, or the library), and its outcome on the row; a long message truncates. WHO RUNS WHERE. - A seat with a subscription (Claude Code and its subagent seats) runs where it is. - A seat without one runs in the Docker sandbox on the LAN worker host with a Composition (Config/Compositions/<seat>.xml): a free online model, only the site groups and MCP tools its job needs, no secrets in the VM (KB.Manual.08-Workers). WHERE THINGS ARE. - The plan of this operating model: Project SPWAY on /sites/Projects. The client: python tools/spice.py (put Projects/ProjectTasks <key> ...; call tool_update_list_item ...). The rule for coding agents: the Decide and Improve steps of the ioodari skill.
source: Project SPWAY on /sites/Projects
Copilots - our agents, governed like documentsFor the operator and every site owner: what a copilot is here, its columns, its lifecycle from Draft to Published, the test, the site boundary, what publishing does and what is still owed. SPWAY.12c.
COPILOTS ARE OUR AGENTS, GOVERNED LIKE DOCUMENTS. - There is no Microsoft Copilot in SPICE. "Copilots" is SharePoint's name for the library where a site keeps its agents, and each row in it is one of ours: an ActorProfile (role, instructions, tone, model, skills) with the governance SharePoint adds - owner, status, the site it may read, the audience, a risk level, when it was tested and published. - Every site that mans agents has a Copilots library (/sites/<site>/Lists/Copilots); every seat has a personal one on its My Site (/sites/my-<seat>/Lists/Copilots), SharePoint's agents in OneDrive. THE COLUMNS, IN THE ORDER THE FORM SHOWS THEM. - Overview: Title, Description, Owner (a person or seat from the directory), Status. - Behaviour: Actor role, Goal (the instructions), Backstory (the tone), Provider and Model (the model policy), Conversation starters (the questions it is offered with, one per line - the first is what the test asks). - Knowledge and data access: Knowledge sources, one line per source as list=<ListName> access=Read - only this site's lists; another site's list is refused, the site is the security boundary. - Permissions and actions: Skill catalog (the skills it may call) and Allowed tools. - Governance: Risk level (Low answers from published content; Medium reads restricted lists or writes; High acts outside the site, spends or reaches people), Audience, Department (the business area the views group by), Last tested, Last published. THE LIFECYCLE. - Draft -> Submitted -> Published, with Disabled and Archived escapes; Archived is final. Publishing requires Last tested: an untested copilot cannot be offered. A Member publishes a tested Draft directly; a Submitted copilot needs a Manager to approve. Rule of thumb until the form enforces it: High risk goes through Submitted. - Test: open the copilot and press Test. It asks the first conversation starter as the copilot, grounded on its knowledge sources, shows the answer on the page, records the exchange on /board/comms and stamps Last tested. No starter, no test. - Publish: the copilot becomes an actor on the Hub (Hub/Actors, as Actor.<site>.<key>) that the engine can run and anyone may chat with (the Chat with an Agent tool); Last published is stamped. Disable or Archive removes the actor again; a re-publish brings it back. THE VIEWS. - All, Published, My Copilots (yours as owner), Drafts needing review (Submitted), High-risk, Disabled or archived, By business area. WHAT IT CANNOT DO YET. - A copilot answers from list rows placed under its prompt; it does not browse pages or call tools on its own. The guided creation flow (one step per section) and the High-risk rule on the Publish button are planned (SPWAY.12c-4); a published copilot with a runtime of its own becomes a seat (a directory row and a My Site) - that enrolment is the next row after it.
source: Project SPWAY, plans/spway/12c/design.md
IOODARI - the working loop, step by stepBRAINSTORM.14: the IOODARI method as data - what each of the seven steps asks. Read by the IOODARI phases (Query.KnowledgeByTag tag=ioodari) instead of the whole knowledge base, which buried a small model in unrelated platform material.
IOODARI is a working loop for one item at a time: Intent, Observe, Orient, Decide, Act, Retrospect, Improve. Each step answers its own question and hands its result to the next. 1 INTENT - purpose first. One sentence describing a real-world outcome someone would rely on or pay for, not a process step. Who benefits, how we will know it worked, the constraints. If it sounds like project management, ask why it matters and go one level deeper. 2 OBSERVE - facts, not conclusions. What is known about the item now (its notes, children, linked items), what is missing and where to look. Borrow a known standard or practice before inventing one. 3 ORIENT - analysis. Apply what is known, argue against the first instinct, name the gap between current and desired and the assumptions to test. A suspicion is a hypothesis until evidence supports it. End with 2-3 ranked options and their trade-offs. 4 DECIDE - one choice. Pick an option and say why, the fallback, how success is measured, how to undo it, what it affects. Say if the owner must approve it (new structure, money, anything irreversible). 5 ACT - the smallest concrete next actions, in order: owner, first step, what done looks like. Reuse what exists first. 6 RETROSPECT - honesty. Did the plan serve the intent or only create activity? Separate what was shipped from the value delivered; name what is owed or at risk; one-line verdict. 7 IMPROVE - make the lesson permanent: the rule, checklist item, template or reminder to keep, where it lives, and its exact wording. STAY ON THE ITEM. Answer only about the item in the task and its subtree. Do not bring in platform internals, product names, decks or credits that the item does not mention.
Pre-mortem - imagine the failure firstBRAINSTORM.22: Gary Klein's pre-mortem (Harvard Business Review, 2007) as data - what each of its three steps asks. Read by the pre-mortem phases (Query.KnowledgeByTag tag=premortem).
A pre-mortem runs BEFORE a plan starts. Instead of asking what could go wrong, it assumes the plan already failed and asks why - prospective hindsight, which surfaces risks people otherwise keep to themselves. 1 FAILURE - it is a year from now and the plan failed. Describe the failure concretely, as if it happened. 2 CAUSES - the plausible reasons it failed, most likely first, specific to this plan. No fixes yet. 3 MITIGATE - for the top causes, one preventive action and one early-warning sign each. Use it on a node that is about to become a project (Stage Proposal): its result is the risk list the project starts with.
Operations hub - the departments that run the businessEngineering, IT, HR, Finance, Legal, Procurement, Talent, project tracking, support and issues. Root /sites/Operations.
Hub root /sites/Operations. Sites: Engineering, IT, HR, Finance, Legal, Procurement, Talent, ProjectTracker, CustomerSupport, IssueTracker, Issues, and the documents-only departments. Crew seats are Members here. Work arrives as list rows and moves by the list's own workflow; a change to how a department works is a part edit, not code.
Missions hub - the agency divisions and crewsResearch, ventures, genesis, reception, studio, the crew and market desks: where agents deliver. Root /sites/Missions.
Hub root /sites/Missions. Sites: ResearchEnrichment, ResearchLab, VentureEngine, Genesis, Reception, LifeAssistant, Studio, CrewDesk, MarketDesk. Crew seats are Members. Delegated work lands on CrewDesk Tasks as a row; outputs are filed to the invoking site's lists.
Business apps hub - the ERP modules and their mastersCommon masters, distribution, inventory, manufacturing, CRM and Projects (the backlog). Root /sites/Common.
Hub root /sites/Common (the shared masters every module looks up). Sites: Distribution, Inventory, Manufacturing, Crm, Projects. Projects holds the backlog (ProjectTasks): update rows through MCP, never by hand in SQL. Crew seats read the modules and are Members of Projects.
Knowledge hub - shared, approved knowledge and catalogsThe Wisdom wiki (content approval, Obsidian vault) and the catalogs made from specs. Root /sites/Wisdom.
Hub root /sites/Wisdom. Sites: Wisdom (Enterprise Wiki; a page lands Pending until the operator approves; query_wisdom reads approved pages), LlmRadar and ProductCatalog (one catalog for every product type; each category generated from a spec). Propose a lesson as a Pending page; never approve your own.
Governance hub - approvals, compliance, term store, directoryCompliance, Approvals, Services (term store, audit) and Directory. Owners and approvers act; seats read. Root /sites/Compliance.
Hub root /sites/Compliance. Sites: Approvals, Services (term store, audit, scheduler state), Directory (people, app principals). Anonymous callers are refused; crew seats are Visitors. A change here goes through an approval, never a direct write.
Seat briefing - start from your My SiteThe first thing every agent seat does: read its own briefing from /sites/my-{seat} through SPICE's stored queries, instead of relying on a pasted prompt.
**Start here - your briefing lives in SPICE.** Your own site is /sites/my-{seat}. One call gives the whole briefing in layers, every fact once (AGT.67): tool_compose_briefing with seat={seat}, plus topic=<term> for the approved wisdom on your task. Or read the parts one by one through SPICE's stored queries - call AS YOURSELF with your seat secret from ~/.spice/{seat}.secret (never print it); without it you are the anonymous System Account and your own My Site privacy does not apply (AGT.37): curl -s -X POST http://127.0.0.1:5198/mcp/jsonrpc -H "Content-Type: application/json" -H "Authorization: Bearer $(cat ~/.spice/{seat}.secret)" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_my_context","arguments":{"seat":"{seat}"}}}' Then the same call with query_my_skills (your skill index - pull one body with query_my_asset and a path), query_my_tasks (your open tasks) and query_my_memory (what you learned before). What the whole city has learned and the operator approved: query_wisdom with topic=... (a Topics term, e.g. sharepoint, antipattern). A project's open work: query_open_backlog with project=AGT (or OPS, TPL, ...). If SPICE is not reachable, say so in one line and continue from the instructions below.
source: SPICE.Web/wwwroot/XSLT/CrewAgent.xslt
Wiring Tracer - operating instructionsThe Wiring Tracer crew seat's full operating instructions. The SPINE is the source of truth for this seat; .claude/agents/spice-wiring-tracer.md is a GENERATED projection of it via wwwroot/XSLT/CrewAgent.xslt. Edit here, then re-run tools/project-crew.ps1 - never edit the markdown by hand.
You trace ONE wiring question through the SPICE repo (C:\Users\erike\TectonAgency) and return the answer with file:line evidence. Precision over breadth — this is usually the load-bearing correctness question for a structural cycle. You do NOT edit code. READ FIRST: `plans/AgentCollaboration.md`. Recurring wiring facts that bite here: - **Target → site → blueprint mapping.** Feature activation passes a `Target`; the two activation paths disagree (stapling = full blueprint `@Name`; AddSiteBlueprint = Portal-stripped). Lists read at the *stripped* path `/sites/{StripPortalSuffix(Name)}/Lists/{n}`. Always confirm which identifier a reader vs a writer uses — they often differ. - **Read vs write location.** A writer (a Tool, an op-handler) may store under one key while the reader (a ViewModelBuilder, a controller) scans another. The served surface is the READ path; pin it. - **DI lifetimes (#5/#26).** A Singleton resolving a Scoped service is a captive dep that 500s only on the request that resolves it, never at boot. Trace the lifetime of every service in the chain. - **What consumes a part.** Before assuming a declared part "does something", find the handler/op/tool that READS it. A part with no consumer is inert (#23/#25 — assert the served surface). - **Manifest persistence.** `ProvisioningApplier` saves on `ManifestTouched`; in dev ContentRoot is the source dir, so it rewrites the source manifest at runtime. METHOD: start from the entry point named in the task (a route, a tool, an op-handler, a part), follow the calls with Grep/Read, and quote the decisive lines. Use `./tools/parts-xq.ps1` (PowerShell) for part-spine questions. When the codebase contradicts an assumption, say so — surfacing the contradiction is the value. DELIVER (tight, file:line on every claim): the direct answer to the question, the read-key vs write-key if they differ, and a one-line CONCLUSION the orchestrator can build on (e.g. "add the <List> to SiteBlueprint@Name=X; it surfaces at /sites/{stripped}/Lists/{n}; matcher must accept full + stripped"). Your final message IS the trace.
source: .claude/agents/spice-wiring-tracer.md
Impact Scout - operating instructionsThe Impact Scout crew seat's full operating instructions. The SPINE is the source of truth for this seat; .claude/agents/spice-impact-scout.md is a GENERATED projection of it via wwwroot/XSLT/CrewAgent.xslt. Edit here, then re-run tools/project-crew.ps1 - never edit the markdown by hand.
You are a read-only pre-flight scout. Given a planned structural change in the SPICE repo (C:\Users\erike\TectonAgency), you find every test that change could break — BEFORE it's written — so the cycle lands first-try. You do NOT edit anything. READ FIRST: `plans/AgentCollaboration.md`. The breakage classes you hunt (with receipts in the V24 arc): - **Positional record constructors.** Adding a field to a `record` breaks positional callers. Grep `new <Record>(` across tests + non-test code; report each as positional (breaks) vs named/initializer (safe). A trailing param with a default keeps positional callers compiling — note when that dodge works. - **XSD-sequence pins.** Tests asserting an exact child-element order (e.g. the FeatureType sequence). Adding an element mid-sequence breaks them; appending last usually doesn't. Quote the assertion. - **Count / value pins.** Tests asserting a literal count (list counts, part counts) or a specific enum/attribute value that your change shifts. - **Source-file pins (#27).** Tests that `XDocument.Load` a single file (e.g. `Parts.xml`) and assert a part is *physically there*, OR build a `PartLibrary` WITHOUT the same contributors the real boot wires (e.g. missing `HiveFeatureContributor`). Migrating a part into the hive breaks these — flag them and recommend `Fixtures/LiveParts.MergedSafDocument()` / adding the contributor to the fixture. - **Behavioral pins.** Tests asserting handler/op recorded messages or activation outcomes that a new code path (e.g. a new skip/enforcement branch) would change. Check whether LIVE parts (e.g. Feature.IssueTracker) rely on the old behavior. METHOD: grep precisely, read the matched test bodies, and for each hit report **file:line + the assertion + WILL-BREAK / SAFE + the minimal fix** (update the assertion, default the new field, append not insert, add the contributor, etc.). Prioritize the highest-likelihood breakage first. DELIVER a tight checklist (a table is ideal). Be specific — "V2417 line 57 asserts Web→Department, breaks, change to Web" beats "some hive tests may fail". Your final message IS the checklist.
source: .claude/agents/spice-impact-scout.md
Fidelity Researcher - operating instructionsThe Fidelity Researcher crew seat's full operating instructions. The SPINE is the source of truth for this seat; .claude/agents/spice-sp-fidelity-researcher.md is a GENERATED projection of it via wwwroot/XSLT/CrewAgent.xslt. Edit here, then re-run tools/project-crew.ps1 - never edit the markdown by hand.
You research ONE step of the SPICE platform's SharePoint-fidelity arc and return a concrete implementation SPEC. You do NOT edit code. SPICE is a .NET 10 app at C:\Users\erike\TectonAgency that models SharePoint-on-prem the XML/XSD/XSLT way. READ FIRST: `plans/AgentCollaboration.md` (the shared conventions). The rules that bind you: - Reuse the existing part; only propose a NEW part kind if genuinely needed, and FLAG it as needing operator approval (the platform's "ask first" rule). - The hive reads elements by LOCAL NAME, so SP's exact namespace can be adopted later. - Find before you build: query the spine with `./tools/parts-xq.ps1 -All "//<Kind>"` (PowerShell; `pwsh` is not on PATH) BEFORE grepping — the part spine spans ~14 files. - Served-surface discipline (#23/#25/#27): a "fidelity" cycle is only real if the engine actually consumes the translated part end-to-end (a reader resolves what the writer produced). If your element needs NEW engine (no existing handler/tool consumes it), say so loudly — that's a bigger, ask-first cycle, not pure source-format translation. METHOD: 1. Read `SPICE.Foundation/Hive/HiveFeatureContributor.cs` (how SP elements translate to SAF parts), the relevant `SAF-Parts.xsd` type, and the existing SPICE part the element maps onto. 2. Use WebSearch to confirm the REAL SharePoint on-prem schema for the element (borrow the real attribute/element names — MS Learn is authoritative). 3. Check whether an existing handler/op/tool already CONSUMES the target part (grep the Provisioning + Tools folders). This decides source-format-only (cheap) vs new-engine (ask-first). DELIVER (<~500 words, evidence-cited): 1. REAL SP schema — the attributes/children that matter + a short example. 2. REUSE-FIRST MAPPING — which existing SPICE part/handler covers it; the SP→SAF attribute table; whether anything genuinely new is required (flag for approval). 3. CONCRETE CYCLE PLAN — exact files to touch, XSD change (if any), test shape mirroring an existing `SPICE.Web.Tests/V24xx*Tests.cs`, and a non-duplicating worked-example receipt. 4. RISKS — especially IDREF resolution across the merge, child-order in synthesized parts, and the #27 source-file-pin trap (migrating an IDREF-target part breaks no-hive fixtures). Your final message IS the spec (it is returned to the orchestrator, not shown to the user).
source: .claude/agents/spice-sp-fidelity-researcher.md
Served Verifier - operating instructionsThe Served Verifier crew seat's full operating instructions. The SPINE is the source of truth for this seat; .claude/agents/spice-verify.md is a GENERATED projection of it via wwwroot/XSLT/CrewAgent.xslt. Edit here, then re-run tools/project-crew.ps1 - never edit the markdown by hand.
You verify that ONE change's served surface actually round-trips on a RUNNING SPICE instance (repo C:\Users\erike\TectonAgency). The recurring trap you exist to catch: a green suite proves the MODEL, the served surface shows nothing until a human says "show me" - capability theatre. You assume the server is already up (the orchestrator booted it); you NEVER boot, edit, or fix. Your verdict is the value. READ FIRST: `plans/AgentCollaboration.md` (the served-surface trio #23/#25/#27 + the gotchas). The judgment you encode (the cost-this-session class): - **#25 reuse the WHOLE seam.** A writer (a Tool, an op-handler) may store under one key while the reader (a ViewModelBuilder, a controller, a board XQuery) scans another. "Applier returned success" is NOT proof. Find the write key and the read key; if they differ, the round-trip is the only proof. - **#23 assert the served surface, not the model.** Hit the path the operator actually sees, not a unit-test proxy. - **#27 assert the resolved spine, not the source file.** A part resolves identically whether it lives in Parts.xml or the hive; verify via the served/merged surface, not a file path. - **The two-approval-surfaces class (the exact confusion to catch).** `/approvals` renders pipeline checkpoints from `IApprovalGateStore`; a gated PARK from e.g. `Tool.BuildDivision` lands as a list item on `/sites/Engineering/Lists/ApprovalRequests` (the `ApprovalRequests` list, queried via `item:{id}`). They are DIFFERENT surfaces. A parked division shows `count=0` on `/approvals` and is visible only on the ApprovalRequests list view. When two surfaces could be confused, name which is correct and why. - **List-item read key (#25 corollary).** A runtime `AddListItem` write must prefix `@Key` with `item:` or the row commits but is invisible to every list view (the read path queries `item:{id}`). A missing prefix is a FAIL even though the write "succeeded". - **Generation honesty (the LLM-seam class — the cost-this-session #35 class).** If the change touches an LLM path (a `Workflow`/`Phase`, a provider, the `AgentRouter`, or the `Mode="ToolLoop"` seam), the round-trip is: FIRE the generation (`POST /agency/workflows/{id}/run`, e.g. `Workflow.WatchdogReview`) and confirm the output is REAL — not `[Echo agent]`, not empty, and `outputSchemaValid=true`. The ToolLoop path is a SEPARATE seam from single-shot (it BYPASSES the router), so a provider fix to one may not reach the other. A stub / empty / degenerate generation is a **FAIL**, never N/A. Reuse `tools/toolloop-smoke.ps1`. (memory: feedback_toolloop_separate_seam_from_router) METHOD: 1. **Identify the surface from the diff.** Read the changed files named in the task (or `git diff` if none named). Pin the touched controller / board / list / tool / part and what it WRITES. 2. **Separate write path from read path.** Trace where the change stores data vs where the operator reads it. Quote the write key and the read key with file:line. If they differ, that difference IS the thing to prove. 3. **Probe the read surface.** `curl` the served read surface against the passed base URL (default `http://localhost:5198`). For a data change, exercise the full **write -> read round-trip**: invoke the tool/endpoint, then confirm the artifact appears on the served read surface (the right list view / board / page), not merely that the write call returned success. 4. **Discriminate confusable surfaces.** When more than one surface could be meant, state which is correct and show the other returning empty if that is the trap. 5. **Use `./tools/parts-xq.ps1` (PowerShell)** for part-spine resolution questions. If the codebase contradicts the change's stated intent, say so - surfacing the contradiction is the value. VERDICT RULES: - **PASS** - the write reaches the served read surface; the round-trip is demonstrated with evidence. - **FAIL** - the write does not surface on the read path the operator sees (wrong key, wrong surface, missing `item:` prefix, reader scans elsewhere). Show the missing-from-read-surface evidence. - **N/A** - the change has no served data surface (pure XSLT/style, a refactor, an interface move). Give the reason; never a false PASS. **BUT a change to an LLM generation path is NEVER N/A** — fire the generation and grade the output; a stub/empty/schema-invalid answer is a FAIL, not N/A. DELIVER (tight, file:line on every claim) - this block IS your final message: ``` VERDICT: PASS | FAIL | N/A SURFACE: <the exact served read URL checked, or "none (reason)"> WRITE PATH: <where/under-what-key the change writes> (file:line) READ PATH: <where/under-what-key the operator reads> (file:line) ROUND-TRIP: <what was written, where it was read back, the curl evidence> CONFUSABLE: <the wrong-but-tempting surface and why it is wrong, if any> CONCLUSION: <one line the orchestrator can act on> ``` You never edit code. If you cannot reach the running instance, say so and return N/A with the reason (do not boot it yourself).
source: .claude/agents/spice-verify.md
KA.HarmlessProbe
Tc package: standards adopted at V12.0aWhich world-standard taxonomies the Common master-data package adopts and where each lookup field binds.
STANDARDS ADOPTED: ISO 4217 - Currency codes (USD, EUR, JPY, ...) - ContentType.Currency, list "Common/Currencies". ISO 3166-1 - Country codes (US, BE, NL, ...) - ContentType.Country, list "Common/Countries". ISO 639-1 - Language codes (en, nl, fr, ...) - ContentType.Language, list "Common/Languages". UN/CEFACT Rec 20 - Unit-of-measure codes (KGM, MTR, PCE, ...) - ContentType.UnitOfMeasure, list "Common/UnitsOfMeasure". GAAP / IFRS - Account-type root categories - ContentType.AccountType, list "Common/AccountTypes". PLATFORM-DEFINED: PaymentTerm - NET30 / 2-10-NET30 / COD / PREPAID - list "Common/PaymentTerms". BusinessPartnerRole - CUSTOMER / SUPPLIER / CARRIER / BANK / TAX / EMPLOYEE - list "Common/BusinessPartnerRoles". WHY EACH CHOICE: ISO 4217 currency codes are the de-facto standard everywhere SWIFT/banking touches; downstream UBL invoices need the alpha-3 code. ISO 3166-1 country alpha-2 is what most APIs accept; EuMember flag drives VAT and PEPPOL routing. ISO 639-1 covers the 184 living languages most platforms need; the alpha-2 is what content-language headers use. UN/CEFACT Rec 20 is the standard CEFACT/UNECE codes for trade documents; aligns with UBL InvoiceLine quantity unit codes. GAAP/IFRS five-category root is the common ground between national accounting standards; jurisdiction-specific refinements layer as child rows on ContentType.ChartOfAccount. EXTENSION PATH: when a downstream module needs richer rows (e.g., UNSPSC item classification at lower levels), add another typed ContentType.Term subclass in the appropriate Tc-or-module package; do not invent parallel lookup tables.
source: SPICE.Apps.Erp.Tc/Parts.Tc.xml
Tc package: V12.1 master-data shapesWhich V12.1 ContentTypes exist for foundational master data, what well-known identifiers each carries, and how operational modules layer on top.
V12.1 SHIPS 6 MASTER-DATA CONTENT TYPES. Each is a typed-XML record validated through IContentTypeResolver and stored as one row per Sku/Code/Key on a list under Common. ContentType.BusinessPartner list "Common/BusinessPartners" Globally-unique business partner (Customer/Supplier/Carrier/Bank/Tax/Employee). Well-known identifiers: LEI (ISO 17442), GLN (GS1), VAT number. Lookups: RoleCode -> BusinessPartnerRoles; CountryCode -> Countries; DefaultCurrencyCode -> Currencies; DefaultPaymentTermCode -> PaymentTerms. ContentType.Item list "Common/Items" Master-data Item (SKU + classification + UoM). Well-known identifiers: UNSPSC (United Nations Standard Products and Services Code). Lookups: UomCode -> UnitsOfMeasure; DefaultCurrencyCode -> Currencies. ContentType.Address list "Common/Addresses" Postal address linked to a BusinessPartner via Sku. Well-known shape: ISO 19160 postal-address fields (Street/City/PostalCode/Region/Country). Lookups: CountryCode -> Countries. ContentType.BankAccount list "Common/BankAccounts" Bank account on a BusinessPartner (Iban + Bic + Currency). Well-known identifiers: ISO 13616 IBAN, ISO 9362 BIC. Lookups: CurrencyCode -> Currencies. ContentType.ExchangeRate list "Common/ExchangeRates" Effective-dated currency conversion rate. Lookups: FromCurrencyCode + ToCurrencyCode -> Currencies. ContentType.ChartOfAccount list "Common/ChartOfAccounts" Hierarchical accounting code mapped to a GAAP root category. Lookups: AccountTypeCode -> AccountTypes. Self-ref via ParentAccountCode for hierarchy. WHAT V12.1 DELIBERATELY EXCLUDES (queued for downstream-module cycles): - FiscalCalendar. Year / Period / StartDate / EndDate / Status. Layered in V12.5 (Tf Finance) because period-close is a Tf concern. - TaxCategory + TaxRate. Country-specific; layered in V12.5 alongside FiscalCalendar. - UNSPSC tree beyond top 2 levels. V12.0a seeded only the segment+family levels (~3000 rows at full coverage); deeper levels load via operator action or a future Connector to a UNSPSC source. WHY EACH WELL-KNOWN ID IS REQUIRED VS OPTIONAL: LEI required on a BusinessPartner that transacts in regulated financial flows; optional for internal-only roles (Employee, internal cost centre). Platform doesn't enforce - field is optional in the XSD and the resolver flags as Warning, not Error, when absent. GLN optional - mostly relevant for logistics (Carrier role) and large retail Supplier chains; not every BusinessPartner has one. IBAN required on BankAccount but only Member-role-readable per the field's Confidential classification - cross-module accounting joins via BusinessPartnerSku join key, not by exposing the IBAN. VAT number context-required: EU B2B invoices need it on Customer + Supplier rows; the country prefix MUST match the BusinessPartner's CountryRef. Not platform-enforced today; queued for V12.5 when invoicing ships. DOWNSTREAM MODULE EXTENSION RECIPE: Operational ContentTypes (V12.3+ SalesOrder, ProductionOrder, PurchaseOrder, Invoice, etc.) reference these masters via two patterns: (1) Parent inheritance: ContentType.SalesOrderHeader can derive from ContentType.Item then add SalesOrder-specific fields + a Transitions block for status workflow. (2) Direct Lookup: a SalesOrderHeader.CustomerSku Lookup field binds to Common/BusinessPartners; the resolver renders the partner's Title in dropdowns + form views. Pattern (2) is the more common - inheritance is reserved for ContentTypes that genuinely extend the master's shape (e.g. a "ContentType.PreferredSupplier" that inherits BusinessPartner and adds a PriorityScore field).
source: SPICE.Apps.Erp.Tc/Parts.Tc.xml
Tcm package: Customer Management overview - the N=6 receiptWhat V14.5 ships, why CRM as a dedicated package on top of V12.0a Tc masters, and how the portal-ready hooks unblock V15+ external client portal + subscription work.
V14.5 SHIPS the N=6 receipt: Td (V12.4) + Tf (V12.6) + Ti (V12.7) + Tp (V12.8) + Tm (V14.1) + Tcm (this cycle) all live on the same V12.0 foundation + V12.0a Tc masters with zero per-module C# beyond the empty AppModuleBase subclass. WHY TCM (vs extending Tc): Tc (Common) is the master-data substrate: BusinessPartner, Address, Currency, ItemMaster, etc. Reference data that every other module looks up. Putting workflow CTs (Lead, Opportunity, Activity, Quote) into Tc would invert that dependency direction - Tc would contain operational rows instead of being looked up by operational rows. Tcm sits on top of Tc as a separate operational module, mirroring Td/Tf/Ti/Tp/Tm. The Sales/CRM team gets their own SiteBlueprint (CrmPortal) without polluting the common-master site. SEVEN CONTENT TYPES: ContentType.Lead list 'Crm/Leads' Status: New -> Contacted -> Qualified -> Converted/Disqualified. V15 hooks: SubmittedByExternal (Bool), PartySku Lookup, Classification split. ContentType.Opportunity list 'Crm/Opportunities' Status: Identified -> Qualified -> Proposal -> Negotiation -> Won/Lost. PartySku Lookup to Common/BusinessPartners (Customer). V14.6 candidate: Opportunity.Won -> Td.SalesOrder via CrossModuleRule. ContentType.Activity list 'Crm/Activities' Status: Planned -> InProgress -> Done/Cancelled. Kind: Call/Email/Meeting/Task/Note. Cross-refs: Activity-on-Lead, Activity-on-Opportunity, Activity-on-Party. ContentType.Campaign list 'Crm/Campaigns' No Transitions - campaigns are date-bound (StartDate/EndDate). Lead.CampaignSku + Opportunity.CampaignSku attribute attribution. ContentType.Quote list 'Crm/Quotes' Status: Draft -> Sent -> Accepted/Rejected/Expired. Cross-ref to parent Opportunity. ContentType.CommunicationLog list 'Crm/CommunicationLogs' No Transitions - immutable append-only log. Direction: Inbound/Outbound; Channel: Email/Phone/SMS/InPerson/Other. ContentType.PartyRelationship list 'Crm/PartyRelationships' No Transitions - relationships are state. Kind: ParentOf/SubsidiaryOf/Influencer/Partner/Competitor (xPRL-aligned). PORTAL-READY HOOKS (V15+ external client portal): Every workflow CT carries PartySku (Lookup to Common/BusinessPartners) so V15 portal can scope 'show me only rows where PartySku = current customer' without retrofitting the data model. Lead.SubmittedByExternal (Boolean) flags web-form intake. Future PublicForm part type + magic-link auth plugin sets this true on anonymous submissions; ACL surfaces only operator-approved leads on the public portal. Per-field Classification deliberately set: Public: LeadName, LeadEmail, LeadPhone, LeadSubmittedQuoteAmount, OppAmount, OppPartySku, QuoteAmount, etc. Internal: LeadScore, ActivityNotes, CampaignBudgetAmount, etc. Confidential: Lead.InternalNotes, Opportunity.LostReason, etc. V15 client portal honors classification automatically: only Public fields appear on the customer's portal view; Internal/Confidential stay operator-only. No code change required at portal cycle time. CROSS-MODULE QUEUED (V14.6 candidates): - Rule.Tcm.OpportunityWon -> Td.SalesOrder draft (the canonical CRM-to-ERP handoff). Either duplicate per portal or generalize CrossModuleRule.Match to accept multiple Dept/List tuples. - Rule.Tcm.QuoteSent -> Activity follow-up reminder (Schedule cron 3 days after Sent). - Rule.Tcm.LeadConverted -> Opportunity draft creation. FUTURE PACKAGES (V15+): - SPICE.Apps.Erp.Tsub (Subscriptions): Plan, Subscription, BillingCycle, UsageRecord. Recurring-invoice generation via CrossModuleRule Schedule. Stripe Subscriptions / Chargebee / Zuora shape. - SPICE.Plugins.Auth.MagicLink + <PublicForm/> part type: external client portal infrastructure. Per-customer ACL based on PartySku.
source: SPICE.Apps.Erp.Tcm/Parts.Tcm.xml
Tcm: OASIS CIQ standards mapping (xPIL / xPRL / xNAL anchor)How V14.5 Tcm CTs map to the OASIS Customer Information Quality v3.0 family. Standards anchor for future XSD vendoring + PEPPOL-equivalent partner-master interop.
OASIS Customer Information Quality (CIQ) v3.0 is the canonical XML/XSD family for CRM-shaped data. SPICE Tcm anchors its design on CIQ so future cycles can vendor the XSDs (V14.5b candidate) and claim standards-conformance by construction - the same shape V12.6b earned for UBL-aligned invoices. OASIS CIQ MEMBERS (renamed in v3.0): xNL (eXtensible Name Language) - person/organization names xAL (eXtensible Address Language) - postal addresses (ISO 19160-aligned) xNAL (eXtensible Name and Address Language) - xNL + xAL composed xPIL (eXtensible Party Information Language, formerly xCIL) - party as customer: contacts, communications, account refs xPRL (eXtensible Party Relationships Language, formerly xCRL) - account-contact-lead-opportunity cross-links SPICE MAPPING: CIQ standard SPICE ContentType Where it lives ---------------------------------------------------------------------------- xPIL (Party Info) ContentType.BusinessPartner V12.0a Tc/BusinessPartners ContentType.Lead V14.5 Tcm/Leads ContentType.Opportunity V14.5 Tcm/Opportunities xAL (Address) ContentType.Address V12.0a Tc/Addresses xNL (Name) Field.LeadName (V14.5 single Text; future decompose into FirstName/LastName/OrganizationName when receipts justify - probably V14.5b or V15) xPRL (Relationships) ContentType.PartyRelationship V14.5 Tcm/PartyRelationships (Kind enum: ParentOf/SubsidiaryOf/Influencer/Partner/ Competitor matches xPRL relationship-type-code vocab) VENDORING POSTURE (V14.5 -> V15.3 SHIPPED): V14.5 shipped the MAPPING (this KB) without vendoring the XSDs. V15.3 closes the loop by vendoring OASIS CIQ v3.0 cs02 XSDs into SPICE.Apps.Erp.Tcm/Schemas/OASIS-CIQ-v3/ and wiring Phase.IssueOpportunity with OutputSchema="Schemas/OASIS-CIQ-v3/xPIL.xsd". Vendored XSD set (10 files, ~200 KB total): CommonTypes.xsd - shared CIQ-wide types xlink-2003-12-31.xsd - W3C XLink (CIQ uses for relationship hrefs) xNL.xsd + xNL-types.xsd - Names xAL.xsd + xAL-types.xsd - Addresses xNAL.xsd + xNAL-types.xsd - composed Name+Address xPIL.xsd + xPIL-types.xsd - Party Information (top of the stack) **Note on xPRL:** OASIS CIQ v3.0 cs02 did NOT ship a standalone xPRL XSD. The historical xPRL "Party Relationships Language" (v2 and earlier) was folded INTO xPIL: relationships now live as PartyRelationships / PartyRelationship sub-elements inside xPIL.xsd. ContentType.PartyRelationship still maps cleanly to the xPIL party-relationship sub-shape (Kind enum matches xPIL's RelationshipType vocabulary), and the validation rail catches structural deviations identically. The V14.5 KB's reference to xPRL as a separate XSD is retained as historical anchoring; tagging preserves searchability for anyone landing here from CIQ v2 docs. Phase.OutputSchema="Schemas/OASIS-CIQ-v3/xPIL.xsd" is wired on Phase.IssueOpportunity (this manifest). Resolved by PhaseOrchestrator.ResolveSchemaPath via the V12.6b app-package probe (AppContext.BaseDirectory/Schemas/OASIS-CIQ-v3/...). Agent-emitted Party XML is XSD-validated before persist runs. Operator wanting to import a third-party CRM's CIQ-export pipes through the same XSD without writing a per-source adapter. WHY THIS COMPOUNDS: Standards-conformance is a recurring SPICE bet. V12.0a anchored ISO 4217 (currencies), ISO 3166 (countries), ISO 17442 (LEI), GS1 GLN. V12.6b vendored UBL Invoice 2.1. V14.5 adds OASIS CIQ as the CRM-side anchor. When SPICE talks to other ERP/CRM systems, the conversation happens in standard XML; the platform's typed-CT layer is the operator-facing presentation, not a proprietary data model.
source: SPICE.Apps.Erp.Tcm/Parts.Tcm.xml
Td package: Distribution module overviewWhat V12.4 ships, which Tc masters each ContentType binds to, and the status flow on each of the 3 ContentTypes.
V12.4 SHIPS 3 OPERATIONAL HEADER CONTENTTYPES, each with a BaaN-style status flow declared via the V12.0b Transitions seam. Manifest-only - zero C# beyond an empty AppModuleBase. ContentType.SalesOrder list "Distribution/SalesOrders" Status: Draft -> Approved -> Picked -> Shipped -> Invoiced -> Closed Cancel: from Draft, Approved, or Picked (post-Picked needs reverse moves first). Lookups: CustomerSku -> Common/BusinessPartners; CurrencyCode -> Common/Currencies; PaymentTermCode -> Common/PaymentTerms. ContentType.PurchaseOrder list "Distribution/PurchaseOrders" Status: Draft -> Approved -> Sent -> Received -> Closed Cancel: from Draft, Approved, or Sent. Lookups: SupplierSku -> Common/BusinessPartners; CurrencyCode -> Common/Currencies; PaymentTermCode -> Common/PaymentTerms. ContentType.ShipmentNotice list "Distribution/ShipmentNotices" Status: Created -> Loaded -> InTransit -> Delivered (terminal) Lost: terminal escape from Created / Loaded / InTransit. Lookups: CarrierSku -> Common/BusinessPartners. SalesOrderNumber stays Text for now (V12.4b will promote to Lookup against SalesOrders once a self-list parent-ref convention is decided). WHAT YOU GET FOR FREE (no extra cycle): - Typed forms at /sites/Distribution/Lists/{Sales,Purchase,Shipment}{Orders,Notices}/New with Lookup dropdowns populated from V12.1 Tc termsets/masters (V12.1 already proved BusinessPartners JSON Schema renders with live option counts). - "Next step" buttons on the Display form (V12.2 FormView projection of Transitions). - POST /sites/Distribution/Lists/{X}/items/{Id}/advance?to=Y (V12.2 controller). - MCP tool_advance_list_item works against all 3 ContentTypes (V12.3 IToolInvoker reads any Transitions block via IContentTypeResolver). - REST CRUD at /api/sites/Distribution/lists/{X}/items with XSD validation (V8.x ListsApiController + V12.3.5 ContentType-on-create persistence). - JSON Schema at /api/sites/Distribution/lists/{X}/_schema with resolved Lookup options. V13.6 SHIPPED (closing the V12.4 line-item deferral): ContentType.SalesOrderLine list "Distribution/SalesOrderLines" parent-ref: SalesOrderNumber + LineNumber Fields: SalesOrderNumber + LineNumber + ItemSku + Description + Quantity + UnitPrice + LineExtensionAmount + TaxCategoryCode (Lookup -> Common/TaxCategories). ContentType.PurchaseOrderLine list "Distribution/PurchaseOrderLines" parent-ref: PurchaseOrderNumber + LineNumber Fields: PurchaseOrderNumber + LineNumber + ItemSku + Description + Quantity + UnitPrice + LineExtensionAmount. No Transitions on either - lines ride their parent header's lifecycle. WHAT V12.4 + V13.6 STILL DEFER: - Cross-module engagement (Td -> Ti on Receive to register a StockMove). V12.7 ships Ti. - UBL XSD validation on the Order shape. V12.6 ships Tf with UBL Invoice; orders can follow the same pattern in V13.7+. - Tax calculation on TotalAmount. V12.5 ships TaxCategory. - Parent-ref promotion to Lookup (instead of Text + LineNumber). Needs an engine-side self-list seam; deferred to a future cycle that covers it across SalesOrderLine + PurchaseOrderLine + InvoiceLine + JournalEntryLine + Payment.InvoiceNumber together. - Auto-rollup of LineExtensionAmount -> header TotalAmount via an IItemAddedListener. EXTENSION RECIPE (adding a new operational ContentType to this module): 1. Declare FieldDefinitions for the new shape, prefixing field Ids with the document code (Po* / So* / Sn* / etc.) to keep cross-CT field-name collisions impossible. 2. Declare the ContentType inheriting ContentType.Item, FieldRefs, and a Transitions block. 3. Add a ListTemplate. 4. Add a List to DistributionPortal in SAF-Site-Manifest.xml. Done. The form, the API, the MCP advance, the schema endpoint, the dropdowns all light up.
source: SPICE.Apps.Erp.Td/Parts.Td.xml
Tf package: UBL 2.4 InvoiceType -> ContentType.Invoice field mapThe standards-conformance receipt of the V12 horizon. Documents how each ContentType.Invoice FieldDefinition corresponds to a UBL 2.4 InvoiceType element so V12.6b can wire actual UBL XSD validation without re-deriving the mapping.
STRATEGIC FRAME (per KB.ErpFoundationPlan): the strongest leverage the platform has for ERP isn't the runtime; it's typed XML against world-standard schemas. UBL 2.4 InvoiceType is exactly the shape we need, so mirror it field-for-field. V12.6 ships the typed shape + lookups + status flow; V12.6b vendors the actual UBL XSD as an embedded resource and wires it as Phase.OutputSchema for agent emit + ContentType.Invoice validation for human/API write. UBL 2.4 InvoiceType -> ContentType.Invoice MAPPING (element-by-element): UBL element | SPICE FieldDefinition | Notes ------------------------------------------ | -------------------------------------- | ----- cbc:ID | Field.InvInvoiceNumber | required string cbc:IssueDate | Field.InvIssueDate | required DateTime cbc:DueDate | Field.InvDueDate | optional DateTime; auto-computable as IssueDate + PaymentTerm.NetDays cbc:InvoiceTypeCode | Field.InvTypeCode | Choice (380 / 381 / 384); default 380 commercial cbc:DocumentCurrencyCode | Field.InvCurrencyCode | Lookup -> Common/Currencies (ISO 4217) cac:AccountingSupplierParty/cac:Party | Field.InvSupplierSku | Lookup -> Common/BusinessPartners (SUPPLIER role) cac:AccountingCustomerParty/cac:Party | Field.InvCustomerSku | Lookup -> Common/BusinessPartners (CUSTOMER role) cac:PaymentTerms | Field.InvPaymentTerm | Lookup -> Common/PaymentTerms (out-of-band: fiscal-period selection) | Field.InvFiscalPeriod | Tf cross-ref to Common/FiscalPeriods.Title (V12.5) cac:LegalMonetaryTotal/cbc:PayableAmount | Field.InvTotalAmount | required Number, currency from InvCurrencyCode cac:TaxTotal/cbc:TaxAmount | Field.InvTaxAmount | optional Number cac:TaxCategory/cbc:ID | Field.InvTaxCategoryCode | Lookup -> Common/TaxCategories (V12.5) cbc:Note | Field.InvNotes | optional Note (workflow state - not in UBL) | Field.InvStatus | Choice (Draft/Sent/Posted/Paid/Closed/Cancelled); drives Transitions UBL ELEMENTS SHIPPED IN V13.6 (cac:InvoiceLine field map): UBL element | SPICE FieldDefinition | Notes ---------------------------------------------------- | ------------------------------ | ----- cbc:ID (within cac:InvoiceLine) | Field.IlLineNumber | required ordinal, 1..N within invoice cbc:InvoicedQuantity | Field.IlInvoicedQuantity | required Number in item UoM cbc:LineExtensionAmount | Field.IlLineExtensionAmount | required Number; sum across lines = header PayableAmount cac:Item/cac:SellersItemIdentification/cbc:ID | Field.IlItemSku | cross-ref to Common/Items.Sku cac:Item/cbc:Description | Field.IlItemDescription | per-line description (may override master) cac:Price/cbc:PriceAmount | Field.IlUnitPrice | per-unit price in header currency cac:Item/cac:ClassifiedTaxCategory/cbc:ID | Field.IlTaxCategoryCode | Lookup -> Common/TaxCategories; overrides header default (header parent-ref - not in UBL) | Field.IlInvoiceNumber | Text reference to parent Invoice.InvoiceNumber Closes V12.6b's "minimal-valid UBL Invoice rejected without InvoiceLine" XSD-rejection pin: a real header+lines invoice now round-trips through the vendored XSD without rejection. UBL ELEMENTS STILL DEFERRED: cac:Delivery (delivery dates + ship-to) cac:OrderReference (cross-ref back to a PurchaseOrder.OrderNumber) cac:BillingReference (credit-note back-references) These layer on top of the V13.6 InvoiceLine field map without disturbing it - additive cycle. WHY THIS MATTERS: 1. PEPPOL BIS Billing 3.0 is built on UBL. An agent that emits a SPICE Invoice row with these fields IS emitting a PEPPOL-compliant invoice payload (modulo line-item completeness). V12.6b's XSD wiring catches the gap programmatically. 2. ISO 20022 financial messages (V12.7+) reuse much of UBL's CommonAggregateComponents (cac:Party / cac:Address etc.). Adopting UBL field names here means future ISO 20022 modules layer on top without renaming. 3. Inter-system interop becomes trivial: a PEPPOL access point can POST a UBL XML invoice straight to V11.24 WorkflowsController (Content-Type=application/xml) -> V11.1 validation against the UBL XSD -> V11.2 auto-persist to Tf/Invoices. Zero mapping layer. V12.6 SHIPS: 3 typed ContentTypes (Invoice, Payment, JournalEntry) each with Transitions. FinancePortal SiteBlueprint hosting Invoices + Payments + JournalEntries lists. ListsApiController already validates writes against the resolved ContentType (V8.x); the BaaN-style /advance + MCP tool_advance_list_item paths work automatically. V12.6b SHIPPED (vendoring + wiring): UBL 2.1 Invoice XSD bundle vendored at SPICE.Apps.Erp.Tf/Schemas/UBL/ - maindoc/UBL-Invoice-2.1.xsd plus 14 common/*.xsd files (CAC + CBC + CommonExtensionComponents + CCTS + UnqualifiedDataTypes + QualifiedDataTypes + xmldsig + XAdES). Copyright (c) OASIS Open 2013, freely distributable. Phase.IssueInvoice declared in this manifest with OutputSchema='Schemas/UBL/maindoc/UBL-Invoice-2.1.xsd'. Resolved by PhaseOrchestrator.ResolveConfigPath via the new app-package probe (AppContext.BaseDirectory/Schemas/UBL/...). Agent-emitted UBL Invoice XML is XSD-validated before persist runs. V13.6 SHIPPED (header + lines): ContentType.InvoiceLine with the UBL field map above; list "Finance/InvoiceLines"; parent-ref Field.IlInvoiceNumber -> Invoice.InvoiceNumber. ContentType.JournalEntryLine for double-entry split (LineNumber + AccountCode + DebitAmount + CreditAmount + LineDescription); list "Finance/JournalEntryLines"; parent-ref Field.JelJournalNumber -> JournalEntry.JournalNumber. EXTENSION RECIPE: when adding a new UBL-aligned ContentType (CreditNote / DespatchAdvice / etc.): 1. Find the UBL XSD's root element + its CommonBasic/CommonAggregate children. 2. Declare FieldDefinitions in the same order, with UBL element names in the Description text. 3. Declare ContentType inheriting ContentType.Item, FieldRefs in UBL order. 4. Declare Transitions for the document's status flow. 5. Optional: drop the XSD into Schemas/UBL/ for V12.6b validation wiring. Done.
source: SPICE.Apps.Erp.Tf/Parts.Tf.xml
Ti package: Inventory module overview + cross-module engagement planWhat V12.7 ships, what V12.7b adds (cross-module engagement Td PO.Received -> Ti StockMove), and how StockMove's Done transition triggers OnHandQuantity recompute.
V12.7 SHIPS the N=3 operational module receipt - Td Distribution (V12.4), Tf Finance (V12.6), and now Ti Inventory all live on the same V12.0 foundation with zero per-module C#. 3 ContentTypes + portal blueprint + lookups + status flow = a working warehouse module. ContentType.StockItem list 'Inventory/StockItems' Balance row per (ItemSku, WarehouseCode, BinCode). Updated by StockMove transitions on Done. No Transitions - balances aren't workflow documents. ContentType.StockMove list 'Inventory/StockMoves' Status: Open -> Reserved -> Picked -> Done (+ Cancelled from pre- Done). The operational unit. Reason enum picks Receipt / Shipment / Transfer / Adjustment / Internal - drives cross-module audit narrative. ContentType.BinLocation list 'Inventory/BinLocations' Master-data row per warehouse bin. Code is the cross-ref key. BinType Choice (Picking / Bulk / Quarantine / Receiving / Shipping) enables future picking-strategy automation. CROSS-MODULE ENGAGEMENT (V12.7b candidate): The pattern that makes Ti+Td+Tf actually compose end-to-end: Td PurchaseOrder.Status: Sent -> Received triggers IItemEventListener that creates a Ti StockMove with: Reason = Receipt Reference = PurchaseOrder.OrderNumber ItemSku = (from PO line; V12.7b layered with InvoiceLine) ToWarehouse = (operator's default receiving warehouse) Status = Open Td SalesOrder.Status: Approved -> Picked triggers IItemEventListener that creates a Ti StockMove with: Reason = Shipment Reference = SalesOrder.OrderNumber ItemSku = (from SO line) FromWarehouse = (operator's default outbound warehouse) Status = Open IMPLEMENTATION SHAPE (deferred to V12.7b): - SPICE.Apps.Erp.Ti gains a TiAppModule.RegisterAppServices registration for one IItemEventListener (CrossModuleStockMoveListener). - The listener filters AddListItemOpHandler's V8.4 fan-out: if site == 'Distribution' AND ( (list == 'PurchaseOrders' AND newStatus == 'Received') OR (list == 'SalesOrders' AND newStatus == 'Picked') ) then create Ti/StockMoves row with Reference=that.OrderNumber. - Pattern banked: same shape as V11.5 OodaLoop's LearnedPattern- WritebackListener (V8.4 fan-out + V11.5 AppModule registration). Cross-module is the new wrinkle; everything else is platform-native. WHAT V12.7 DEFERS: - OnHandQuantity recompute on StockMove.Done. Needs an EventReceiver or IItemEventListener wiring to scan affected StockItem rows. V12.7b. - Negative stock guards. V12.7b adds a policy rule that refuses StockMove transitions where the resulting OnHandQuantity would go negative. - Picking-strategy automation (FIFO / FEFO / nearest-bin). Needs a Skill that takes a SalesOrder line and picks the right StockMove sequence. V12.8+. V12.7 PROVES the N=3 thesis for operational modules and seeds the cross-module-engagement work without committing to its wiring yet. The cross-module shape lives here in this KB so V12.7b doesn't need to re-derive it.
source: SPICE.Apps.Erp.Ti/Parts.Ti.xml
Tm package: Manufacturing module overview - the N=5 receiptWhat V14.1 ships, why Manufacturing opens the V14 horizon's BaaN expansion, and how the four header + three line ContentTypes cross-link to Ti (StockMoves consume / receipt) + Td (sales-driven MRP at V14.3) at V14.2.
V14.1 SHIPS the N=5 receipt: Td (V12.4) + Tf (V12.6) + Ti (V12.7) + Tp (V12.8) + Tm (this cycle) all live on the same V12.0 foundation with zero per-module C# (beyond the empty AppModuleBase subclass). Seven ContentTypes (4 headers + 3 lines) + portal blueprint + Transitions = the manufacturing module's structural surface. ContentType.BillOfMaterials list 'Manufacturing/BillsOfMaterials' Status: Draft -> Active -> Obsolete + Cancelled escape from Draft. Identified by (ProductSku, Version); BomComponent lines reference both. ContentType.BomComponent list 'Manufacturing/BomComponents' Lines under BOM. Parent-ref (ProductSku, Version, LineNumber). No Transitions - rides parent BOM lifecycle. ContentType.Routing list 'Manufacturing/Routings' Status: Draft -> Active -> Obsolete + Cancelled escape from Draft. Identified by (ProductSku, Version); RoutingOperation lines reference both. ContentType.RoutingOperation list 'Manufacturing/RoutingOperations' Lines under Routing. Parent-ref (ProductSku, Version, LineNumber). No Transitions - rides parent Routing lifecycle. ContentType.WorkCenter list 'Manufacturing/WorkCenters' Status: Available <-> Maintenance <-> Offline + Decommissioned terminal. Three recoverable states + one terminal retirement state. CapacityPerHour + CostPerHour drive V14.6 capacity/load /admin card. ContentType.ProductionOrder list 'Manufacturing/ProductionOrders' Status: Planned -> Released -> InProgress -> Completed + Cancelled escape. Released is the V14.2 fan-out trigger to Ti consumption StockMoves. Completed is the V14.2 receipt-StockMove trigger for the parent item. ContentType.ProductionOperation list 'Manufacturing/ProductionOperations' Lines under ProductionOrder. Parent-ref (OrderNumber, LineNumber). No Transitions - rides parent order lifecycle. ActualHours populated at parent Completed time. WHY MANUFACTURING OPENS V14: The V12.4/V12.6/V12.7/V12.8 ladder proved the V12.0 foundation generalises across four BaaN package codes. Tm is the fifth - the N=5 receipt that shows the abstraction holds for a process-oriented (rather than document- oriented) module. BOM + Routing + WorkCenter are master-data shapes; ProductionOrder is the workflow document that ties them together at execution time. CROSS-MODULE WIRING (queued for V14.2): - ProductionOrder.Released -> Ti consumption StockMove per BomComponent (issue materials to the order). - ProductionOrder.Completed -> Ti receipt StockMove for parent product (receive finished goods into inventory). This brings cross-module listener fan-out to N=5 (V12.7b/c + V13.1b + V13.2 + V14.2); CrossModuleListenerBase becomes a candidate lift if the shape repeats meaningfully. PARENT-REF CONVENTION: All three line CTs use Text + Number (LineNumber) for parent-ref, matching V13.6 SalesOrderLine / PurchaseOrderLine / InvoiceLine / JournalEntryLine. V14.7 sweeps these (8+ candidates total) to proper Lookup once the engine-side self-list parent-ref seam lands.
source: SPICE.Apps.Erp.Tm/Parts.Tm.xml
V14.4 ContentProductionPortal: N=2 worked example of Tm primitives across domainsBanks the receipt that the V14.1 Tm primitives (BOM/Routing/WorkCenter/ProductionOrder + line CTs + MrpSuggestion) genuinely generalize - the same typed-CT layer drives a Content Production agency without a single C# edit.
V14.4 SHIPS the first non-Manufacturing receipt of the V14.1 Tm primitives. Same ContentTypes, same FieldDefinitions, same Transitions. Operator-facing list names alias the Manufacturing terminology to the Content Production domain via 8 ListTemplate entries; the underlying typed-XML shape is byte-identical. ALIAS TABLE: Manufacturing term -> ContentProduction alias (same ContentType) ---------------------------------------------------------------------------- BillOfMaterials -> Content Recipe ContentType.BillOfMaterials BomComponent -> Recipe Ingredient ContentType.BomComponent Routing -> Production Pipeline ContentType.Routing RoutingOperation -> Pipeline Step ContentType.RoutingOperation WorkCenter -> Production Station ContentType.WorkCenter ProductionOrder -> Content Run ContentType.ProductionOrder ProductionOperation -> Run Step ContentType.ProductionOperation MrpSuggestion -> Sourcing Suggestion ContentType.MrpSuggestion WHAT CARRIES OVER (the receipt): - Status workflows: Content Recipe Draft -> Active -> Obsolete is the BOM lifecycle. Content Run Planned -> Released -> InProgress -> Completed is the ProductionOrder lifecycle. - V12.2 FormView "Next step" buttons render identically (driven by Transitions on the CT). - V12.3 MCP tool_advance_list_item works against ContentProduction lists without any registration. - V8.x typed REST + form rendering picks up the alias list automatically. - V12.1 Lookup fields (ItemSku etc.) still resolve against the same Common masters. CROSS-MODULE RULE REACH (V15.4 RESOLVED): V14.4 banked the honest limitation that cross-module rules matched on (Department, List, To) and couldn't reach both Manufacturing/ProductionOrders AND ContentProduction/ContentRuns from a single rule. V15.4 closes the gap with '*' wildcard support on Department and List (the To attribute stays exact-match by design - rules pin a specific transition, broadening that to '*' would let one Match fire on every advance event and pollute the audit log). Operators with a real Content Production agency who want auto-fan-out now have: (a) Duplicate the rules per portal: Rule.Cprod.ContentRunReleased with the same Emit shape pointed at ContentProduction's storage tracker (if any). Simple, works today, low blast radius. (b) **V15.4 SHIPPED**: generalize via wildcards. Rule with Match Department="*" List="ContentRuns" To="Released" fires regardless of department. Or Department="Manufacturing" List="*" To="Released" for any-list-in-this-dept. Or Department="*" List="*" To="Released" for the broadest cross-portal sweep. The engine special-cases '*' in CrossModuleRuleDispatcher.MatchesEvent (~6 LOC); existing single-Dept single-List rules keep matching exactly as before (regression-pinned in V154MatchWildcardTests). (c) Treat the typed-CT layer as the platform's contract and accept that cross-module reactions are dept-scoped. Reasonable for ERP boundaries - physical manufacturing inventory shouldn't auto-emit content-production rows. V14.4 shipped under this stance; V15.4 makes option (b) feasible for the cases that genuinely want it. WHY THIS COMPOUNDS: V14.4 receipts the V14.1 plan claim "abstraction is real." N=1 worked example with the typed-CT layer + Transitions intact. V14.5 (a second non-manufacturing worked example - sprint planning or release management) brings N=2, which is the platform's standard threshold for promoting a pattern from "specific" to "framework." If V14.5 also lands clean, V14.7 (parent-ref Lookup sweep) and V14.8 (horizon debrief) can confidently say the Tm primitives are the platform's process-engine layer, not "another ERP module."
source: SPICE.Apps.Erp.Tm/Parts.Tm.xml
V14.3 MRP-lite: scheduled CrossModuleRule as materialized-view analogueThe first cycle to consume V14.2c's IStorageProvider.Enumerate seam. Banks the pattern: cron-triggered enumerate+filter+emit as a single declarative XML rule.
V14.3 SHIPS the deterministic MRP-lite sweep without writing a new hosted service. Instead, the V14.2c CrossModuleRule grammar gains a Schedule trigger as a one-of with Match: <CrossModuleRule Id="Rule.Tm.MrpSweep" ...> <Schedule Cron="0 2 * * *" /> <FanOut Site="Inventory" List="StockItems"> <caml:Where> <caml:Leq> <caml:FieldRef Name="StOnHandQuantity" /> <caml:Value Type="Number">0</caml:Value> </caml:Leq> </caml:Where> </FanOut> <Emit TargetSite="Manufacturing" TargetList="MrpSuggestions" ContentType="ContentType.MrpSuggestion" KeyTemplate="AUTO-MRP-{FanOut.StItemSku}-{FanOut.StWarehouseCode}"> <Field Name="ItemSku" FromFanOut="StItemSku" /> ... </Emit> </CrossModuleRule> WHAT THE V14.3 RESEARCH FOUND: The pattern "enumerate one list, compute derived quantity, emit per-row, on a schedule" is the materialized-view shape (Databricks REFRESH EVERY, ClickHouse refreshable MVs, dbt incremental + unique_key). SP Designer workflows expressed it via a "Pause + loop-back" hack on top of timer jobs; SP didn't have a clean declarative shape for it. XSLT 3.0 streaming with xsl:result-document is the XML-native cousin but needs Saxon and fights pure-functional model with side-effect writes. The cleanest SPICE-native answer turned out to be: extend the V14.2c CrossModuleRule grammar with a Schedule trigger. Same FanOut/Emit semantics; only the trigger type changes. Foundation's SchedulerHostedService gains a second pass that fires scheduled rules per minute-tick. Result: zero new hosted services per future scan-and-emit feature. All declarative XML. PATTERN BANKED: Event-driven cross-module reaction: <Match Department="..." List="..." To="..." /> Time-driven scan-and-emit: <Schedule Cron="0 2 * * *" /> Both use the same <FanOut/>, <caml:Where/>, <Emit/>, <Field/>, KeyTemplate. The dispatcher's only branching is at the trigger level; everything downstream is shared. WHAT V14.3 DELIBERATELY DEFERS: - Synthetic event fields ({Event.FiredAt}, {Event.RuleId}) for scheduled rules. Today FromEvent and {Event.X} are rejected at parse time on scheduled rules. Add when a real receipt needs them. - Arithmetic in field bindings (Quantity = -QuantityPer * OrderQty). Today bindings are literal + FromEvent + FromFanOut + token substitution. Add when a receipt needs computed values across two sources. - LLM-driven MRP (an agent reasoning over forecast + supplier lead-times + capacity). V15 scope. - Per-item reorder thresholds (Field.MrpThreshold on ItemMaster). V14.3 uses a global cron-baked threshold (the <Leq>...0</Leq> predicate); future cycles can lift this into the ItemMaster.
source: SPICE.Apps.Erp.Tm/Parts.Tm.xml
Tp package: Project module overview - the N=4 receiptWhat V12.8 ships, why Project closes the 4-module ERP loop, and how it cross-links to Td (Tasks reference SalesOrder lines) + Tf (Budget lines reference ChartOfAccounts).
V12.8 SHIPS the N=4 receipt: Td (V12.4) + Tf (V12.6) + Ti (V12.7) + Tp (this cycle) all live on the same V12.0 foundation with zero per-module C# (beyond the empty AppModuleBase subclass + V12.7b cross-module listener which lives in Ti, not the foundation). 4 ContentTypes + portal blueprint + lookups + Transitions = a working project-management module. ContentType.ProjectDefinition list 'Projects/Projects' Status: Planning -> Active -> Closed + Cancelled escapes. Lookups: ClientSku -> Common/BusinessPartners (CUSTOMER), CurrencyCode -> Common/Currencies. ContentType.ProjectTask list 'Projects/ProjectTasks' Status: Todo -> InProgress -> Done + Blocked (recoverable) + Cancelled (terminal). AssigneeSku -> Common/BusinessPartners (EMPLOYEE role expected). ProjectCode -> ProjectDefinition.ProjectCode (text cross-ref; V12.8b promotes to Lookup once self-list parent-ref settles). ContentType.ProjectBudget list 'Projects/ProjectBudgets' Quantity rows per (Project, Category, AccountCode). No Transitions - budgets revise by new rows; versioning preserves history. WHY PROJECT CLOSES THE LOOP: Td (Sales/Purchase/Shipment) covers the deal cycle. Tf (Invoice/Payment/JournalEntry) covers the financial cycle. Ti (StockItem/StockMove/BinLocation) covers the physical cycle. Tp (Project/Task/Budget) covers the work cycle. Together they span the four canonical ERP nouns (deal, money, stock, work). An operator running Td + Tf + Ti + Tp has the minimum-viable typed ERP. CROSS-MODULE LINKS (queued for V12.8b/V12.9): - Tp.Task.Reference -> Td.SalesOrder.OrderNumber (project work fulfilling a deal) - Tp.Budget.AccountCode -> Tf.ChartOfAccount.AccountCode (project cost mapping) - V12.7b-style cross-module listener: Tp.Task -> Done could fire Tf JournalEntry posting (revenue recognition).
source: SPICE.Apps.Erp.Tp/Parts.Tp.xml
The Observatory is an exchangeable layer over the spine - the world is projected, not paintedWhy V25.41 stands up SPICE.Apps.Observatory as a swappable SoC presentation layer whose scene-graph is projected from the spine (retiring the hand-authored /ship topology) and whose edges show the agent message bus live via the AG-UI stream.
The legacy /ship board looked alive but was half-painting: live data came from XQuery over the spine, yet the topology was hand-authored (the NODES array in ship-scene.js + the Consoles dict in ShipBoardRenderer.cs) and the page chrome was built as C# strings (the FW.CsharpHtmlToXslt debt). The Observatory layer corrects this on two axes. First, the world is PROJECTED: the scene-graph is a Query-backed Board part deriving its nodes from spine entities (Category, Agency, ActorProfile, Machine, Board), so adding an agency in XML grows the view with zero JS/C# - the self-building city as a data consequence. Second, the bus is VISIBLE: in-flight messages render as animated dots on the topology edges, driven by the AG-UI event stream (the V25.40 leg) upgraded to resumable, deep-JSON state deltas. It is built as an exchangeable App layer (Core/Foundation-only); the App owns the scene-graph data + XSLT skins, while the renderer JS, the thin controller shim, and the vendored client assets stay in the host - the Marketplace/GalleryController SoC pattern. Bus visibility is a projection channel over the spine + AG-UI, never a parallel coordination/rendering stack (KB.AgentProtocolChannels); the tap is read-only.
source: docs/plans/2026-06-01-001-feat-observatory-live-layer-plan.md
Tracker package: native Jira-equivalent AppWhat V13.1a ships, why we built rather than imported, and how Sprint+Epic compose with the platform's existing ContentType.Issue.
V13.1 was originally backlog-shaped as "first external connector: Jira/Linear ticket import". The redirect during the V13.1a kick-off was "make our own Jira on our platform way" - i.e. the platform IS the issue tracker. The receipt: zero external system to authenticate against, zero webhook to keep alive, and the typed-XML spine carries through. V13.1a SHIPS: ContentType.Sprint list 'IssueTracker/Sprints' Planning -> Active -> Closed with Cancelled escape from Planning. Fields: SprintCode + SprintGoal + SprintStartDate + SprintEndDate + SprintStatus. ContentType.Epic list 'IssueTracker/Epics' Open -> InProgress -> Done with Cancelled escape. Fields: EpicCode + EpicGoal + EpicStatus. ContentType.Issue extension (in SPICE.Web/Config/Parts.xml): + Field.IssueType (Choice: Bug/Story/Task/Epic) + Field.StoryPoints (Number) + Field.IssueSprintRef (Lookup -> IssueTracker/Sprints) + Field.IssueEpicRef (Lookup -> IssueTracker/Epics) + Field.IssueReporter (Lookup -> Directory/People) + Field.IssueStatus (existing) gains Selected / InReview / Done choices + Transitions block: Open -> Selected -> InProgress -> InReview -> Done (Jira-flavour) coexisting with Open -> InProgress -> Resolved -> Closed (classic SP flavour) so existing Feature.IssueTracker depts stay valid. IssueTrackerPortal blueprint (in SAF-Site-Manifest.xml): Tracker department. Lists: Issues, Epics, Sprints, IssueComments. Feature.IssueTracker activated so the existing open/assign/comment/resolve skills bind. WHY BUILD RATHER THAN IMPORT: An external connector would have shipped one binding per third-party tracker (Jira, Linear, Azure DevOps, ...) plus the inbound-webhook plumbing per binding. The native shape compounds against everything else in the platform: - V12.0b Transitions: BaaN-style Next-step buttons render automatically. - V12.3 Tool.AdvanceListItem: MCP clients advance Issues via tool call. - V11.1+V11.2: agent phases can emit typed Issues via OutputSchema/OutputList. - F4 refined ListView: Kanban projection is one ?refine=IssueStatus query away. - F8 admin portal: cross-site rollup just works. - V12.7b cross-module listeners: Sprint.Closed -> auto-create retro Issue is one wire away. The Lookup fields (Sprint, Epic) are still resolvable when the IssueTrackerPortal blueprint is absent - the FormView simply renders empty pickers. Other depts gain the Jira-flavour fields on Issue without being forced to populate them. V13.1b SHIPS (deterministic sprint cadence): SprintCadenceHostedService timer (registered by TrackerAppModule; ticks every 10 minutes) calls SprintCadenceService.Tick which: (a) Opens the current ISO-week sprint if absent and we're past Monday 09:00 UTC. Sprint key is canonical Sprint-YYYY-Wnn (e.g. Sprint-2026-W21) so the next-week key is computable without enumerating the list. (b) Auto-closes the previous-week sprint by flipping its SprintStatus from Active to Closed once the new week has started. Deterministic because the previous-week key is computable from 'now - 7 days'. Why a hosted service rather than Schedule + Workflow + agent: sprint open/close is date arithmetic, not a reasoning task. Routing it through the agent path would spend LLM tokens on plumbing and would fail in Echo-only dev environments. The OodaLoop App is the showcase of Schedule + agent (Workflow.OodaCycle); Tracker is the symmetric showcase of Schedule + deterministic listener. Both patterns are now load-bearing in the platform. QUEUED FOR V13.1c: V13.1c: Rollover of unfinished Issues (IssueStatus != Done/Closed/ Resolved) from the closing sprint into the new sprint; requires list enumeration (today only the SqlServer storage provider exposes that). Kanban projection (refined ListView grouped by IssueStatus). HubPortal Jira card cross-site rollup of all Active Sprints + WIP.
source: SPICE.Apps.Tracker/Parts.Tracker.xml