[sessionmgr][refactor] Add barebones StoryModel*

... and associated tests.  This includes implementations for:
* StoryModel table
* StoryModelMutation union
* StoryModelOwner
* StoryMutator
* StoryObserver

... and an interface for StoryModelStorage (what will ultimately
have a Ledger-backed implementation).

TEST=story_model_owner_unittest,apply_commands_unittest
MF-88 #comment [sessionmgr][refactor] Add barebones StoryModel*

Change-Id: I6daf15afad17a18e18a62efdd8e4d8cdb757c3f9
diff --git a/bin/sessionmgr/story/model/BUILD.gn b/bin/sessionmgr/story/model/BUILD.gn
new file mode 100644
index 0000000..c01950f
--- /dev/null
+++ b/bin/sessionmgr/story/model/BUILD.gn
@@ -0,0 +1,117 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//peridot/build/tests_package.gni")
+
+group("model") {
+  deps = [
+    ":story_observer",
+    ":story_mutator",
+    "//peridot/public/fidl/fuchsia.modular.storymodel",
+  ]
+}
+
+tests_package("story_model_unittests") {
+  deps = [
+    ":apply_mutations_unittest",
+    ":story_model_owner_unittest",
+  ]
+}
+
+source_set("story_observer") {
+  sources = [
+    "story_observer.h",
+  ]
+
+  public_deps = [
+    "//peridot/public/fidl/fuchsia.modular.storymodel",
+  ]
+}
+
+source_set("story_mutator") {
+  sources = [
+    "story_mutator.cc",
+    "story_mutator.h",
+  ]
+
+  public_deps = [
+    "//peridot/public/fidl/fuchsia.modular.storymodel",
+  ]
+}
+
+source_set("story_model_storage") {
+  sources = [
+    "story_model_storage.h",
+    "story_model_storage.cc",
+  ]
+
+  public_deps = [
+    "//peridot/public/fidl/fuchsia.modular.storymodel",
+  ]
+}
+
+source_set("apply_mutations") {
+  sources = [
+    "apply_mutations.cc",
+    "apply_mutations.h",
+  ]
+
+  public_deps = [
+    "//peridot/public/fidl/fuchsia.modular.storymodel",
+  ]
+
+  deps = [
+    "//garnet/public/lib/fxl",
+  ]
+}
+
+executable("apply_mutations_unittest") {
+  testonly = true
+
+  sources = [
+    "apply_mutations_unittest.cc",
+  ]
+
+  deps = [
+    ":apply_mutations",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gtest_main",
+  ]
+}
+
+source_set("story_model_owner") {
+  sources = [
+    "story_model_owner.cc",
+    "story_model_owner.h",
+  ]
+
+  public_deps = [
+    "//peridot/public/fidl/fuchsia.modular.storymodel",
+    "//garnet/public/lib/fxl",
+    "//garnet/public/lib/async_promise",
+  ]
+
+  deps = [
+    ":apply_mutations",
+    ":story_model_storage",
+    ":story_mutator",
+    ":story_observer",
+  ]
+}
+
+executable("story_model_owner_unittest") {
+  testonly = true
+
+  sources = [
+    "story_model_owner_unittest.cc",
+  ]
+
+  deps = [
+    ":story_model_owner",
+    ":story_model_storage",
+    "//garnet/public/lib/gtest",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gtest_main",
+  ]
+}
diff --git a/bin/sessionmgr/story/model/README.md b/bin/sessionmgr/story/model/README.md
new file mode 100644
index 0000000..72b7cd6
--- /dev/null
+++ b/bin/sessionmgr/story/model/README.md
@@ -0,0 +1,103 @@
+### StoryModel
+
+The `StoryModel` FIDL table is used to represent the state of a story.
+`sessionmgr` keeps a separate `StoryModel` in memory for each running story,
+and also persists changes to it onto storage.  Mutations to `StoryModel` are
+done using the `StoryMutator` interface and changes from these mutations
+are notified to registered observers (using the `StoryObserver`
+interface).
+
+This directory defines classes that define the control flow for mutating a
+`StoryModel` FIDL table.
+
+The following interfaces are defined:
+
+* `StoryMutator`: Allows clients to issue a series of `StoryModelMutation`
+  structs that describe mutations to a `StoryModel`.
+* `StoryObserver`: Used to ready `StoryModel` and observe changes to it.
+  Changes to `StoryModel` may happen through `StoryMutator` or, in the
+  case of a distributed storage implementation such as Ledger, from sync'ing
+  with peers.
+interface. Allows clients to read the current `StoryModel` state, and register
+for updates when the `StoryModel` changes.
+
+Both of the above are abstract base classes to aid in testing: clients that
+wish to mutate and/or observe a `StoryModel` will accept a `StoryMutator`
+or `StoryObserver` as a constructor argument.  Making them abstract allows
+injection of test implementations that do not require the full machinery
+introduced by `StoryModelOwner`.
+
+#### `StoryModelOwner` class
+
+`StoryModelOwner` is has its own implementations of `StoryMutator` and
+`StoryObserver`, and coordinates the flow of data from
+`StoryMutators` through a `StoryModelStorage`, applies those changes
+to a `StoryModel` and flows this new model to `StoryObserver` instances.
+It acts as a factory for its own implementations of `StoryMutators` and
+`StoryObservers`.
+
+#### `StoryModelStorage` interface
+
+Supplied to `StoryModelOwner` at the time of its creation, a
+`StoryModelStorage` is responsible for consuming mutation commands and
+updating its persistent storage layer by applying a set of mutation commands,
+as well as notifying of mutations that have been applied. A request to mutate
+does not necessary result in those exact mutations being observed (in the case
+of conflict resolution), nor do observed mutations imply a request was made (in
+the case of a `StoryModel` backed by distributed storage).
+
+A "no-op" StoryModelStorage (one that does not result in any disk-backed
+or other durable storage) would immediately notify of any incoming mutations
+without applying them anywhere. In this case the `StoryModel` would be resident
+in memory only.
+
+#### Flow of Control
+
+Mutation commands flow from anything that has a `StoryMutator` through a
+`StoryModelOwner`, `StoryModelStorage` and are translated into new
+`StoryModels`. From that point forward, observers see the new `StoryModel`
+values.
+
+```
+[some system] -> StoryMutator   ->  StoryModelOwner         |  commands
+                                                  |              |  "
+                                       StoryModelStorage    |  "
+                                                  |              |  "
+[other system] <- StoryObserver <-  StoryModelOwner         $  model
+```
+
+### Example
+
+The constructor for your average System that both mutates and observes a
+`StoryModel` would look like:
+```
+class Foo : public System {
+ public:
+  Foo(std::unique_ptr<StoryMutator> mutator,
+      std::unique_ptr<StoryObserver> observer, ...);
+};
+```
+
+In production, we will create this by leveraging the `StoryModelOwner` to
+create those dependencies:
+
+```
+// assume StoryModelOwner has already been defined.
+auto foo_system = std::make_unique<Foo>(owner->NewMutator(), owner->NewObserver(), ...);
+```
+
+For testing, we leverage test versions of both:
+```
+auto test_mutator = std::make_unique<TestStoryMutator>();
+auto test_observer = std::make_unique<TestStoryObserver>();
+
+// Retain pointers to both |test_mutator| and |test_observer| so we can trigger
+// behavior and validate side-effects.
+auto mutator_ptr = test_mutator.get();
+auto observer_ptr = test_observer.get();
+auto foo_system = std::make_unique<Foo>(std::move(test_mutator), std::move(test_observer));
+
+observer_ptr->NotifyOfModel(new_model);  // Push a new model to observers!
+// |foo_system| should have generated new mutations as a side-effect.
+EXPECT_TRUE(3, mutator_ptr->GetNumCommandsIssued());
+```
\ No newline at end of file
diff --git a/bin/sessionmgr/story/model/apply_mutations.cc b/bin/sessionmgr/story/model/apply_mutations.cc
new file mode 100644
index 0000000..fefa18e
--- /dev/null
+++ b/bin/sessionmgr/story/model/apply_mutations.cc
@@ -0,0 +1,42 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <lib/fxl/logging.h>
+
+#include "peridot/bin/sessionmgr/story/model/apply_mutations.h"
+
+using fuchsia::modular::StoryVisibilityState;
+using fuchsia::modular::storymodel::StoryModel;
+using fuchsia::modular::storymodel::StoryModelMutation;
+
+namespace modular {
+
+namespace {
+
+void ApplySetVisibilityState(const StoryVisibilityState visibility_state, StoryModel* story_model) {
+  story_model->set_visibility_state(visibility_state);
+}
+
+}  // namespace
+
+StoryModel ApplyMutations(const StoryModel& current_model,
+                          const std::vector<StoryModelMutation>& commands) {
+  StoryModel new_model;
+  fidl::Clone(current_model, &new_model);
+
+  for (const auto& command : commands) {
+    switch (command.Which()) {
+      case StoryModelMutation::Tag::kSetVisibilityState:
+        ApplySetVisibilityState(command.set_visibility_state(), &new_model);
+        break;
+      default:
+        FXL_LOG(FATAL) << "Unsupported StoryModelMutation: "
+                       << fidl::ToUnderlying(command.Which());
+    }
+  }
+
+  return new_model;
+}
+
+}  // namespace modular
diff --git a/bin/sessionmgr/story/model/apply_mutations.h b/bin/sessionmgr/story/model/apply_mutations.h
new file mode 100644
index 0000000..e8a77ca
--- /dev/null
+++ b/bin/sessionmgr/story/model/apply_mutations.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_BIN_SESSIONMGR_STORY_MODEL_APPLY_MUTATIONS_H_
+#define PERIDOT_BIN_SESSIONMGR_STORY_MODEL_APPLY_MUTATIONS_H_
+
+#include <fuchsia/modular/storymodel/cpp/fidl.h>
+
+namespace modular {
+
+// Returns a new StoryModel which is the result of applying |commands| to
+// |current_model|.
+fuchsia::modular::storymodel::StoryModel ApplyMutations(
+    const fuchsia::modular::storymodel::StoryModel& current_model,
+    const std::vector<fuchsia::modular::storymodel::StoryModelMutation>
+        &commands);
+
+}  // namespace modular
+
+#endif  // PERIDOT_BIN_SESSIONMGR_STORY_MODEL_APPLY_MUTATIONS_H_
diff --git a/bin/sessionmgr/story/model/apply_mutations_unittest.cc b/bin/sessionmgr/story/model/apply_mutations_unittest.cc
new file mode 100644
index 0000000..3e708af
--- /dev/null
+++ b/bin/sessionmgr/story/model/apply_mutations_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuchsia/modular/storymodel/cpp/fidl.h>
+#include <lib/fit/bridge.h>
+#include <lib/fit/function.h>
+#include <lib/fit/single_threaded_executor.h>
+
+#include "gtest/gtest.h"
+#include "peridot/bin/sessionmgr/story/model/apply_mutations.h"
+
+using fuchsia::modular::StoryVisibilityState;
+using fuchsia::modular::storymodel::StoryModel;
+using fuchsia::modular::storymodel::StoryModelMutation;
+
+namespace modular {
+namespace {
+
+// Test a single StoryModelMutation.set_visibility_state command to change
+// StoryModel.visibility_state.
+TEST(ApplyMutationsTest, SingleMutation_set_visibility_state) {
+  StoryModel before;
+  *before.mutable_visibility_state() = StoryVisibilityState::DEFAULT;
+
+  std::vector<StoryModelMutation> commands(1);
+  commands[0].set_set_visibility_state(StoryVisibilityState::IMMERSIVE);
+  auto result = ApplyMutations(before, commands);
+  EXPECT_EQ(StoryVisibilityState::IMMERSIVE, *result.visibility_state());
+}
+
+// Test two StoryModelMutation.set_visibility_state commands to change
+// StoryModel.visibility_state to one value and back. Tests that multiple
+// commands in a list are applied in order.
+TEST(ApplyMutationsTest,
+     MultipleMutations_AppliedInOrder_set_visibility_state) {
+  StoryModel before;
+
+  std::vector<StoryModelMutation> commands(2);
+  commands[0].set_set_visibility_state(StoryVisibilityState::IMMERSIVE);
+  commands[1].set_set_visibility_state(StoryVisibilityState::DEFAULT);
+  auto result = ApplyMutations(before, commands);
+  EXPECT_EQ(StoryVisibilityState::DEFAULT, *result.visibility_state());
+}
+
+}  // namespace
+}  // namespace modular
diff --git a/bin/sessionmgr/story/model/story_model_owner.cc b/bin/sessionmgr/story/model/story_model_owner.cc
new file mode 100644
index 0000000..6fe687d
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_model_owner.cc
@@ -0,0 +1,159 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "peridot/bin/sessionmgr/story/model/story_model_owner.h"
+
+#include <lib/fit/bridge.h>
+#include <lib/fit/defer.h>
+#include <lib/fxl/logging.h>
+#include "peridot/bin/sessionmgr/story/model/apply_mutations.h"
+#include "peridot/bin/sessionmgr/story/model/story_mutator.h"
+#include "peridot/bin/sessionmgr/story/model/story_model_storage.h"
+
+using fuchsia::modular::storymodel::StoryModel;
+using fuchsia::modular::storymodel::StoryModelMutation;
+using fuchsia::modular::storymodel::ModuleModel;
+
+namespace modular {
+
+namespace {
+// Sets default values for all fields of a new StoryModel.
+void InitializeModelDefaults(StoryModel* model) {
+  model->set_visibility_state(fuchsia::modular::StoryVisibilityState::DEFAULT);
+  model->set_modules(fidl::VectorPtr<ModuleModel>::New(0));
+}
+}  // namespace
+
+// Delegates Execute() to the StoryModelOwner.
+class StoryModelOwner::Mutator : public StoryMutator {
+ public:
+  Mutator(fxl::WeakPtr<StoryModelOwner> weak_owner) : weak_owner_(weak_owner) {}
+  ~Mutator() override = default;
+
+ private:
+  // |StoryMutator|
+  fit::consumer<> ExecuteInternal(std::vector<StoryModelMutation> commands) override {
+    if (!weak_owner_) {
+      fit::bridge<> bridge;
+      bridge.completer.complete_error();
+      return std::move(bridge.consumer);
+    }
+    return weak_owner_->ExecuteCommands(std::move(commands));
+  }
+
+  fxl::WeakPtr<StoryModelOwner> weak_owner_;
+};
+
+// Manages the lifecycle of multiple listener callbacks. When Observer dies,
+// all callbacks registered with RegisterListener() are unregistered from the
+// backing StoryModelOwner.
+class StoryModelOwner::Observer : public StoryObserver {
+ public:
+  Observer(fxl::WeakPtr<StoryModelOwner> weak_owner)
+      : weak_owner_(weak_owner) {}
+  ~Observer() {
+    // If our owner is gone, all of the listener functions have already been
+    // cleaned up. We need to cancel all the deferred actions since they
+    // capture and make a call on our owner.
+    if (!weak_owner_) {
+      for (auto& i : deferred_cleanup_) {
+        i.cancel();
+      }
+    }
+  }
+
+ private:
+  void RegisterListener(
+      fit::function<void(const StoryModel&)> listener) override {
+    if (!weak_owner_) {
+      return;
+      // |listener| is destroyed.
+    }
+
+    deferred_cleanup_.push_back(
+        weak_owner_->RegisterListener(std::move(listener)));
+  }
+
+  const StoryModel& model() override {
+    FXL_CHECK(weak_owner_);
+    return weak_owner_->model_;
+  }
+
+  fxl::WeakPtr<StoryModelOwner> weak_owner_;
+  // When we are destroyed, we want to clean up any listeners we've added to
+  // |shared_state_->owner|.
+  std::vector<fit::deferred_action<fit::function<void()>>> deferred_cleanup_;
+};
+
+StoryModelOwner::StoryModelOwner(
+    fit::executor* executor,
+    std::unique_ptr<StoryModelStorage> model_storage)
+    : model_storage_(std::move(model_storage)),
+      weak_ptr_factory_(this),
+      executor_(executor) {
+  FXL_CHECK(model_storage_ != nullptr);
+  InitializeModelDefaults(&model_);
+  model_storage_->SetObserveCallback(
+      [this](std::vector<StoryModelMutation> commands) {
+        HandleObservedMutations(std::move(commands));
+      });
+}
+
+StoryModelOwner::~StoryModelOwner() = default;
+
+std::unique_ptr<StoryMutator> StoryModelOwner::NewMutator() {
+  return std::make_unique<Mutator>(weak_ptr_factory_.GetWeakPtr());
+}
+
+std::unique_ptr<StoryObserver> StoryModelOwner::NewObserver() {
+  return std::make_unique<Observer>(weak_ptr_factory_.GetWeakPtr());
+}
+
+fit::deferred_action<fit::function<void()>> StoryModelOwner::RegisterListener(
+    fit::function<void(const StoryModel&)> listener) {
+  auto it = listeners_.insert(listeners_.end(), std::move(listener));
+  return fit::defer(
+      fit::function<void()>([this, it] { listeners_.erase(it); }));
+}
+
+fit::consumer<> StoryModelOwner::ExecuteCommands(
+    std::vector<StoryModelMutation> commands) {
+  // fit::bridge allows this function to return (and eventually complete) a
+  // promise that is owned by the caller and still schedule a promise as a task
+  // to execute the model update locally.
+  //
+  // If the caller chooses to ignore the result, our local promise will still be
+  // scheduled and executed.
+  fit::bridge<> bridge;
+  auto promise = model_storage_->Execute(std::move(commands))
+                     .then([completer = std::move(bridge.completer)](
+                               fit::result<>& result) mutable {
+                       if (result.is_ok()) {
+                         completer.complete_ok();
+                       } else {
+                         completer.complete_error();
+                       }
+                     });
+
+  executor_->schedule_task(std::move(promise));
+  return std::move(bridge.consumer);
+}
+
+void StoryModelOwner::HandleObservedMutations(
+    std::vector<StoryModelMutation> commands) {
+  // This is not thread-safe. We rely on the fact that
+  // HandleObservedMutations() will only be called on a single thread.
+  StoryModel old_model;
+  FXL_CHECK(fidl::Clone(model_, &old_model) == ZX_OK);
+  model_ = ApplyMutations(std::move(old_model), std::move(commands));
+
+  executor_->schedule_task(fit::make_promise([this] {
+                             for (auto& listener : listeners_) {
+                               listener(model_);
+                             }
+                             return fit::ok();
+                           }).wrap_with(scope_));
+}
+
+}  // namespace modular
diff --git a/bin/sessionmgr/story/model/story_model_owner.h b/bin/sessionmgr/story/model/story_model_owner.h
new file mode 100644
index 0000000..2286a4b
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_model_owner.h
@@ -0,0 +1,115 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MODEL_OWNER_H_
+#define PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MODEL_OWNER_H_
+
+#include <fuchsia/modular/storymodel/cpp/fidl.h>
+#include <lib/async_promise/executor.h>
+#include <lib/fit/defer.h>
+#include <lib/fit/scope.h>
+#include <lib/fxl/macros.h>
+#include <lib/fxl/memory/weak_ptr.h>
+
+#include <list>
+#include <memory>
+
+#include "peridot/bin/sessionmgr/story/model/story_mutator.h"
+#include "peridot/bin/sessionmgr/story/model/story_observer.h"
+
+namespace modular {
+
+class StoryModelStorage;
+
+// Owns a single instance of StoryModel and manages the flow of control from a
+// stream of mutations to observers.
+//
+// Clients do not depend on or have any direct knowledge of StoryModelOwner.
+// Rather, they depend on either or both a StoryObserver and
+// StoryMutator, depending on if they need to observe or mutate the model.
+//
+// See README.md.
+//
+// This class is not thread-safe.
+class StoryModelOwner {
+ public:
+  // Uses |executor| to schedule internal mutation tasks. Delegates mutation
+  // commands to and reacts to observation of applied mutations from
+  // |model_storage|.
+  explicit StoryModelOwner(fit::executor* executor,
+                           std::unique_ptr<StoryModelStorage> model_storage);
+  ~StoryModelOwner();
+
+  // Returns a mutator object that can be provided to a System that requires
+  // the ability to issue commands to mutate the model. This can be provided to
+  // Systems as a constructor argument.
+  //
+  // The returned StoryMutator may outlive |this|, but will return an
+  // error for all attempts to mutate the model.
+  std::unique_ptr<StoryMutator> NewMutator();
+
+  // Returns an object that can be used to register observer callbacks to the
+  // be notified of the model's current state when changes are made. This
+  // should be provided to Systems as a constructor argument.
+  //
+  // The returned StoryObserver may outlive |this|. See the documentation
+  // for StoryObserver for caveats.
+  std::unique_ptr<StoryObserver> NewObserver();
+
+ private:
+  class Mutator;
+  class Observer;
+
+  // Registers |listener| to be called whenever mutation commands are applied
+  // to |model_|. Returns a deferred action that will deregister |listener|
+  // when it goes out of scope.
+  //
+  // Called by instances of Observer.
+  fit::deferred_action<fit::function<void()>> RegisterListener(
+      fit::function<void(const fuchsia::modular::storymodel::StoryModel&)>
+          listener);
+
+  // Calls |model_storage_| to execute |commands|.
+  //
+  // Called by instances of Mutator.
+  fit::consumer<> ExecuteCommands(
+      std::vector<fuchsia::modular::storymodel::StoryModelMutation> commands);
+
+  // Applies |commands| to |model_| and notifies all |listeners_| with the
+  // updated StoryModel.
+  //
+  // Called indirectly by |model_storage_| through a callback.
+  void HandleObservedMutations(
+      std::vector<fuchsia::modular::storymodel::StoryModelMutation> commands);
+
+  std::unique_ptr<StoryModelStorage> model_storage_;
+
+  // The most recent StoryModel value. Accessed by StoryObservers at any
+  // time. Updated by HandleObservedMutations().
+  fuchsia::modular::storymodel::StoryModel model_;
+
+  // Used to signal to instances of StoryMutator/Observer when |this| is
+  // destroyed.
+  fxl::WeakPtrFactory<StoryModelOwner> weak_ptr_factory_;
+
+  // A list<> so that we can get stable iterators for cleanup purposes. See
+  // RegisterListener().
+  std::list<
+      fit::function<void(const fuchsia::modular::storymodel::StoryModel&)>>
+      listeners_;
+
+  fit::executor* executor_;  // Not owned.
+
+  // Since we schedule our fit::promises for execution on |executor_|, which can
+  // outlive |this|, we use this to wrap our fit::promises (using
+  // fit::promise.wrap_with(scope_)) such that when |this| is destroyed, all
+  // pending promises are abandoned.
+  fit::scope scope_;
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(StoryModelOwner);
+};
+
+}  // namespace modular
+
+#endif  // PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MODEL_OWNER_H_
diff --git a/bin/sessionmgr/story/model/story_model_owner_unittest.cc b/bin/sessionmgr/story/model/story_model_owner_unittest.cc
new file mode 100644
index 0000000..c71b6da
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_model_owner_unittest.cc
@@ -0,0 +1,247 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <lib/fit/bridge.h>
+#include <lib/fit/function.h>
+#include <lib/fit/single_threaded_executor.h>
+
+#include "lib/gtest/test_loop_fixture.h"
+#include "peridot/bin/sessionmgr/story/model/story_model_owner.h"
+#include "peridot/bin/sessionmgr/story/model/story_model_storage.h"
+
+using fuchsia::modular::StoryVisibilityState;
+using fuchsia::modular::storymodel::StoryModel;
+using fuchsia::modular::storymodel::StoryModelMutation;
+
+namespace modular {
+namespace {
+
+// This persistence system acts as a mock for calls to Execute(), and promotes
+// Observe() from protected to public so that we can call it directly from the
+// test body.
+class TestPersistenceSystem : public StoryModelStorage {
+ public:
+  struct ExecuteCall {
+    std::vector<StoryModelMutation> commands;
+
+    // Call this after a call to Execute() to complete the returned promise.
+    fit::completer<> completer;
+  };
+  std::vector<ExecuteCall> calls;
+
+  fit::promise<> Execute(std::vector<StoryModelMutation> commands) override {
+    fit::bridge<> bridge;
+
+    // Store the arguments we got.
+    ExecuteCall call{.commands = std::move(commands),
+                    .completer = std::move(bridge.completer)};
+    calls.push_back(std::move(call));
+
+    return bridge.consumer.promise();
+  }
+
+  using StoryModelStorage::Observe;
+};
+
+class StoryModelOwnerTest : public ::gtest::TestLoopFixture {
+ public:
+  StoryModelOwnerTest() : TestLoopFixture() { ResetExecutor(); }
+
+  std::unique_ptr<StoryModelOwner> Create() {
+    auto model_storage = std::make_unique<TestPersistenceSystem>();
+    model_storage_ = model_storage.get();
+
+    auto owner = std::make_unique<StoryModelOwner>(executor_.get(),
+                                                   std::move(model_storage));
+    return owner;
+  }
+
+  void ResetExecutor() { executor_.reset(new async::Executor(dispatcher())); }
+
+  TestPersistenceSystem* model_storage() { return model_storage_; }
+
+ private:
+  std::unique_ptr<async::Executor> executor_;
+  TestPersistenceSystem* model_storage_;
+};
+
+TEST_F(StoryModelOwnerTest, SuccessfulMutate) {
+  // Show that a single mutation flows through the StoryModelOwner to
+  // StoryModelStorage, and then applies the resulting commands.
+  auto owner = Create();
+
+  auto mutator = owner->NewMutator();
+  bool done{false};
+  auto result_task =
+      mutator->set_visibility_state(StoryVisibilityState::IMMERSIVE)
+          .promise()
+          .and_then([&] { done = true; })
+          .or_else([] { FAIL(); });
+  RunLoopUntilIdle();
+
+  // The persistence system should have been called.
+  ASSERT_EQ(1lu, model_storage()->calls.size());
+  ASSERT_EQ(1lu, model_storage()->calls[0].commands.size());
+  EXPECT_TRUE(model_storage()->calls[0].commands[0].is_set_visibility_state());
+  EXPECT_EQ(StoryVisibilityState::IMMERSIVE, model_storage()
+                                                  ->calls[0]
+                                                  .commands[0]
+                                                  .set_visibility_state());
+
+  // Complete the pending persistence call.
+  model_storage()->calls[0].completer.complete_ok();
+  RunLoopUntilIdle();
+  fit::run_single_threaded(std::move(result_task));
+  EXPECT_TRUE(done);
+
+  // The existing model hasn't changed, because the StoryModelStorage
+  // has not heard back from its storage that the mutation occurred.
+  auto observer = owner->NewObserver();
+  EXPECT_EQ(StoryVisibilityState::DEFAULT, *observer->model().visibility_state());
+
+  // Now dispatch mutations from the persistence system, and we should observe
+  // that ApplyMutations() is invoked.
+  model_storage()->Observe(
+      std::move(model_storage()->calls[0].commands));
+
+  // And the new model value that ApplyMutations returned should be reflected in
+  // the owner.
+  EXPECT_EQ(StoryVisibilityState::IMMERSIVE,
+            *observer->model().visibility_state());
+}
+
+TEST_F(StoryModelOwnerTest, FailedMutate) {
+  auto owner = Create();
+  auto mutator = owner->NewMutator();
+  bool task_executed{false};
+  bool saw_error{false};
+  auto result_task =
+      mutator->set_visibility_state(StoryVisibilityState::IMMERSIVE)
+          .promise()
+          .and_then([&] { task_executed = true; })
+          .or_else([&] { saw_error = true; });
+  RunLoopUntilIdle();
+  model_storage()->calls[0].completer.complete_error();
+  RunLoopUntilIdle();
+  fit::run_single_threaded(std::move(result_task));
+  EXPECT_FALSE(task_executed);
+  EXPECT_TRUE(saw_error);
+}
+
+TEST_F(StoryModelOwnerTest, AbandonedMutate) {
+  // If for some reason the underlying mutation is abandoned, we should observe
+  // an error.
+  auto owner = Create();
+  auto mutator = owner->NewMutator();
+  bool task_executed{false};
+  bool saw_error{false};
+  auto result_task =
+      mutator->set_visibility_state(StoryVisibilityState::IMMERSIVE)
+          .promise_or(fit::error())  // turn abandonment into an error.
+          .and_then([&] { task_executed = true; })
+          .or_else([&] { saw_error = true; });
+  RunLoopUntilIdle();
+  // Clearing the list of calls will destroy the completer, which has the
+  // side-effect of abandoning the returned task.
+  model_storage()->calls.clear();
+  RunLoopUntilIdle();
+  fit::run_single_threaded(std::move(result_task));
+  EXPECT_FALSE(task_executed);
+  EXPECT_TRUE(saw_error);
+}
+
+TEST_F(StoryModelOwnerTest, MutatorLifecycle_OwnerDestroyed) {
+  // When the StoryModelOwner is destroyed but someone is still holding onto a
+  // StoryMutator that mutator should return an error on Execute().
+  auto owner = Create();
+  auto mutator = owner->NewMutator();
+  owner.reset();
+  bool task_executed{false};
+  bool saw_error{false};
+  auto result_task =
+      mutator->set_visibility_state(StoryVisibilityState::IMMERSIVE)
+          .promise()
+          .and_then([&] { task_executed = true; })
+          .or_else([&] { saw_error = true; });
+  RunLoopUntilIdle();
+  fit::run_single_threaded(std::move(result_task));
+  EXPECT_FALSE(task_executed);
+  EXPECT_TRUE(saw_error);
+}
+
+TEST_F(StoryModelOwnerTest, ObserversAreNotified) {
+  // One can create an observer and learn of the new state.
+  auto owner = Create();
+  auto mutator = owner->NewMutator();
+  auto observer = owner->NewObserver();
+
+  bool got_update1{false};
+  observer->RegisterListener([&](const StoryModel& model) {
+    got_update1 = true;
+    EXPECT_EQ(StoryVisibilityState::IMMERSIVE, *model.visibility_state());
+  });
+
+  // Another listener should also get the update!
+  bool got_update2{false};
+  observer->RegisterListener(
+      [&](const StoryModel& model) { got_update2 = true; });
+
+  // Also on another observer.
+  auto observer2 = owner->NewObserver();
+  bool got_update3{false};
+  observer2->RegisterListener(
+      [&](const StoryModel& model) { got_update3 = true; });
+
+  std::vector<StoryModelMutation> commands;
+  commands.resize(1);
+  commands[0].set_set_visibility_state(StoryVisibilityState::IMMERSIVE);
+  model_storage()->Observe(std::move(commands));
+  RunLoopUntilIdle();
+  EXPECT_TRUE(got_update1);
+  EXPECT_TRUE(got_update2);
+  EXPECT_TRUE(got_update3);
+}
+
+TEST_F(StoryModelOwnerTest, ObserversLifecycle_ClientDestroyed) {
+  // When the client destroys its observer object, it no longer receives
+  // updates.
+  auto owner = Create();
+  auto mutator = owner->NewMutator();
+  auto observer = owner->NewObserver();
+
+  bool got_update{false};
+  observer->RegisterListener(
+      [&](const StoryModel& model) { got_update = true; });
+
+  std::vector<StoryModelMutation> commands;
+  commands.resize(1);
+  commands[0].set_set_visibility_state(StoryVisibilityState::IMMERSIVE);
+  model_storage()->Observe(std::move(commands));
+
+  observer.reset();
+  RunLoopUntilIdle();
+  EXPECT_FALSE(got_update);
+}
+
+TEST_F(StoryModelOwnerTest, ObserversLifecycle_OwnerDestroyed) {
+  // When the StoryModelOwner is destroyed, clients can learn of the fact by
+  // using a fit::defer on the listener callback.
+  auto owner = Create();
+  auto mutator = owner->NewMutator();
+  auto observer = owner->NewObserver();
+
+  bool destroyed{false};
+  observer->RegisterListener([defer = fit::defer([&] { destroyed = true; })](
+                                 const StoryModel& model) {});
+
+  owner.reset();
+  EXPECT_TRUE(destroyed);
+
+  // Explicitly destroy |observer| to ensure that its cleanup isn't affected by
+  // |owner| being destroyed.
+  observer.reset();
+}
+
+}  // namespace
+}  // namespace modular
diff --git a/bin/sessionmgr/story/model/story_model_storage.cc b/bin/sessionmgr/story/model/story_model_storage.cc
new file mode 100644
index 0000000..b37a571
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_model_storage.cc
@@ -0,0 +1,27 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "peridot/bin/sessionmgr/story/model/story_model_storage.h"
+
+#include <lib/fxl/logging.h>
+
+namespace modular {
+
+StoryModelStorage::StoryModelStorage() = default;
+StoryModelStorage::~StoryModelStorage() = default;
+
+void StoryModelStorage::SetObserveCallback(
+    fit::function<
+        void(std::vector<fuchsia::modular::storymodel::StoryModelMutation>)>
+        callback) {
+  observe_callback_ = std::move(callback);
+}
+
+void StoryModelStorage::Observe(
+    std::vector<fuchsia::modular::storymodel::StoryModelMutation> commands) {
+  FXL_DCHECK(observe_callback_);
+  observe_callback_(std::move(commands));
+}
+
+}  // namespace modular
diff --git a/bin/sessionmgr/story/model/story_model_storage.h b/bin/sessionmgr/story/model/story_model_storage.h
new file mode 100644
index 0000000..f4ce297
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_model_storage.h
@@ -0,0 +1,60 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MODEL_STORAGE_H_
+#define PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MODEL_STORAGE_H_
+
+#include <fuchsia/modular/storymodel/cpp/fidl.h>
+#include <lib/async_promise/executor.h>
+#include <lib/fit/function.h>
+#include <lib/fxl/macros.h>
+
+#include <list>
+#include <memory>
+
+namespace modular {
+
+// The purpose of a StoryModelStorage is to apply StoryModelMutations to a
+// persistent storage layer and flow observed changes from the storage layer to
+// Observe().
+//
+// Due to the nature of some storage systems (such as Ledger), a call to
+// Mutate() doesn't necessarily result in an equivalent Observe().
+//
+// A trivial example: two peers, A and B, both call Mutate() with conflicting
+// instructions. Peer A learns of peer B's mutation before calling Observe(),
+// and conflict resolution results in no change to the model. In this case,
+// Observe() will not be called.
+class StoryModelStorage {
+ public:
+  StoryModelStorage();
+  virtual ~StoryModelStorage();
+
+  // Registers a callback that is called when Observe() is called by
+  // implementers.
+  void SetObserveCallback(
+      fit::function<
+          void(std::vector<fuchsia::modular::storymodel::StoryModelMutation>)>
+          callback);
+
+  virtual fit::promise<> Execute(
+      std::vector<fuchsia::modular::storymodel::StoryModelMutation>
+          commands) = 0;
+
+ protected:
+  // Calls to Observe() must always be made from the same thread: ordering
+  // of the observed mutations matters.
+  void Observe(std::vector<fuchsia::modular::storymodel::StoryModelMutation>);
+
+ private:
+  fit::function<void(
+      std::vector<fuchsia::modular::storymodel::StoryModelMutation>)>
+      observe_callback_;
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(StoryModelStorage);
+};
+
+}  // namespace modular
+
+#endif  // PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MODEL_STORAGE_H_
diff --git a/bin/sessionmgr/story/model/story_mutator.cc b/bin/sessionmgr/story/model/story_mutator.cc
new file mode 100644
index 0000000..f89c9ca
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_mutator.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "peridot/bin/sessionmgr/story/model/story_mutator.h"
+
+using fuchsia::modular::storymodel::StoryModelMutation;
+
+namespace modular {
+
+StoryMutator::StoryMutator() = default;
+StoryMutator::~StoryMutator() = default;
+
+fit::consumer<> StoryMutator::set_visibility_state(
+    fuchsia::modular::StoryVisibilityState state) {
+  std::vector<StoryModelMutation> commands(1);
+  commands[0].set_set_visibility_state(state);
+  return ExecuteInternal(std::move(commands));
+}
+
+}  // namespace modular
diff --git a/bin/sessionmgr/story/model/story_mutator.h b/bin/sessionmgr/story/model/story_mutator.h
new file mode 100644
index 0000000..c0f24a6
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_mutator.h
@@ -0,0 +1,57 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MUTATOR_H_
+#define PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MUTATOR_H_
+
+#include <fuchsia/modular/storymodel/cpp/fidl.h>
+#include <lib/fit/bridge.h>
+#include <lib/fxl/macros.h>
+
+namespace modular {
+
+// Implemented by and created by the StoryModelOwner.
+//
+// An instance of StoryMutator is provided to all clients that wish to perform
+// mutations on a StoryModel. It is responsible for consuming mutation commands
+// (exposed as methods publicly and converted to StoryModelMutation internally)
+// and dispatching them be applied to a shared StoryModel instance.
+//
+// This is an interface in order to aid in testing clients that depend on
+// StoryMutator.
+class StoryMutator {
+ public:
+  StoryMutator();
+  virtual ~StoryMutator();
+
+  // The following mutators issue a single mutation instruction to
+  // change the StoryModel.
+  //
+  // The returned fit::consumer<> will eventually be completed with the result
+  // of the mutation operation, HOWEVER success does not guarantee that
+  // observers of the StoryModel will see those same changes reflected, and thus
+  // clients should NOT perform side-effects under that assumption.
+  //
+  // It IS safe to perform side-effects once mutations have been observed
+  // through StoryObserver.
+  //
+  // A failure guarantees that the mutation was not applied and it is safe to
+  // retry.
+
+  // Sets the value of |StoryModel.visibility_state|.
+  fit::consumer<> set_visibility_state(
+      fuchsia::modular::StoryVisibilityState state);
+
+ private:
+  // Executes |commands| in order and in a single transaction.
+  virtual fit::consumer<> ExecuteInternal(
+      std::vector<fuchsia::modular::storymodel::StoryModelMutation>
+          commands) = 0;
+
+  FXL_DISALLOW_COPY_AND_ASSIGN(StoryMutator);
+};
+
+}  // namespace modular
+
+#endif  // PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_MUTATOR_H_
diff --git a/bin/sessionmgr/story/model/story_observer.h b/bin/sessionmgr/story/model/story_observer.h
new file mode 100644
index 0000000..f1eb211
--- /dev/null
+++ b/bin/sessionmgr/story/model/story_observer.h
@@ -0,0 +1,50 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_OBSERVER_H_
+#define PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_OBSERVER_H_
+
+#include <fuchsia/modular/storymodel/cpp/fidl.h>
+#include <lib/fxl/macros.h>
+
+namespace modular {
+
+// Implemented bv StoryModelOwner. A client of StoryObserver can query the
+// current state of a StoryModel or register a callback to be notified of
+// changes.
+class StoryObserver {
+ public:
+  StoryObserver() = default;
+  virtual ~StoryObserver() = default;
+
+  // Registers |listener| as a callback for StoryModel updates. If |this| is
+  // destroyed, or the StoryModelOwner that created |this| is destroyed, all
+  // registered listeners will also be destroyed.
+  //
+  // Clients may learn that the StoryModelOwner is destroyed by deferring
+  // a callback in the capture list of |listener|. Example:
+  //
+  //   auto on_abandoned = [] { /* do some cleanup */ };
+  //   observer.RegisterListener(
+  //       [on_destroy = fit::defer(on_abandoned)]
+  //       (const StoryModel& model) {
+  //         /* do something with the update */
+  //       });
+  //
+  // Note that the received StoryModel is only valid for the lifetime
+  // of the call to |listener|.
+  virtual void RegisterListener(
+      fit::function<void(const fuchsia::modular::storymodel::StoryModel& model)>
+          listener) = 0;
+
+  // Returns the current state of the StoryModel.
+  virtual const fuchsia::modular::storymodel::StoryModel& model() = 0;
+
+ private:
+  FXL_DISALLOW_COPY_AND_ASSIGN(StoryObserver);
+};
+
+}  // namespace modular
+
+#endif  // PERIDOT_BIN_SESSIONMGR_STORY_MODEL_STORY_OBSERVER_H_
diff --git a/bin/sessionmgr/story_runner/README.md b/bin/sessionmgr/story_runner/README.md
index eb75d22..e0fc842 100644
--- a/bin/sessionmgr/story_runner/README.md
+++ b/bin/sessionmgr/story_runner/README.md
@@ -1,39 +1,8 @@
-This directory contains implementations of services used by the current story
-runner implementation. They will be revised.
+NOTE: The contents of this directory will slowly migrate into:
 
-What happens so far:
+../story/model
+../story/system
+../story/fidl
 
-1. The story runner app exposes the StoryRunner service that will start a story.
-   The running story is represented as a Story.
-
-2. The Story service allows to request to run an application. Invoking an
-   application through the Story service causes (2.1) the specified application
-   to be run, (2.2) the Module service of that application to be requested, and
-   (2.3.) Initialize() method of the Module service to be invoked with a handle
-   of the Story service instance as a parameter (among others). Thus, the module
-   so started is able to start more modules, all in the scope of the same
-   Story.
-
-3. The Story service also exposes a factory for instances of the fuchsia::modular::Link service. A
-   fuchsia::modular::Link instance exposes an API to store and retrieve values, and to register
-   callbacks to notify when values stored in the instance change. A fuchsia::modular::Link
-   instance is shared between each pair of requesting and requested modules in
-   the story.
-
-4. The Module and fuchsia::modular::Link instances in the story are recorded in the Ledger by the
-   story runner.
-
-5. An existing story can be restarted.
-
-What is still missing:
-
-6. The fuchsia::modular::Link instance holds data according to a Schema.
-
-Miscellaneous observations on these interfaces:
-
-There is a trade off between Interface request arguments and Interface return
-values. Interface requests are less verbose because they are synchronous.
-Interface requests can be sent to handles and the handles be passed on
-immediately. However, if the receiving side is to make calls on the bound
-implementation and delegate implementation to a further service, then this is
-possible only for a returned interface.
+during the process of a refactor. Please avoid adding new contents
+here.
diff --git a/packages/tests/modular_unittests b/packages/tests/modular_unittests
index 0d05e4c..1725f22 100644
--- a/packages/tests/modular_unittests
+++ b/packages/tests/modular_unittests
@@ -4,6 +4,7 @@
         "//peridot/bin/agents/clipboard:clipboard_unittests",
         "//peridot/bin/basemgr:basemgr_unittests",
         "//peridot/bin/sessionmgr/entity_provider_runner:entity_provider_runner_unittests",
+        "//peridot/bin/sessionmgr/story/model:story_model_unittests",
         "//peridot/bin/sessionmgr/puppet_master:puppet_master_unittests",
         "//peridot/bin/sessionmgr/story_runner:story_runner_unittests",
         "//peridot/bin/sessionmgr/storage:storage_unittests",
diff --git a/public/fidl/fuchsia.modular.internal/BUILD.gn b/public/fidl/fuchsia.modular.internal/BUILD.gn
index c91ae2d..06a0781 100644
--- a/public/fidl/fuchsia.modular.internal/BUILD.gn
+++ b/public/fidl/fuchsia.modular.internal/BUILD.gn
@@ -8,8 +8,8 @@
   cpp_legacy_callbacks = true
 
   sources = [
-    "story_data.fidl",
     "sessionmgr.fidl",
+    "story_data.fidl",
   ]
   deps = [
     "//garnet/public/fidl/fuchsia.auth",
diff --git a/public/fidl/fuchsia.modular.internal/story_data.fidl b/public/fidl/fuchsia.modular.internal/story_data.fidl
index b429de2..ed1f50f 100644
--- a/public/fidl/fuchsia.modular.internal/story_data.fidl
+++ b/public/fidl/fuchsia.modular.internal/story_data.fidl
@@ -7,11 +7,13 @@
 using fuchsia.ledger;
 using fuchsia.modular;
 
+// Metadata and summary information about a single story.  Does not contain the
+// data necessary to run a story: see story_model.fidl for that.
 struct StoryData {
     // Metadata available to the SessionShell.
     fuchsia.modular.StoryInfo story_info;
 
-    // An optiona client-supplied name for this story.
+    // An optional client-supplied name for this story.
     string? story_name;
 
     // Story metadata and configuration.
diff --git a/public/fidl/fuchsia.modular.storymodel/BUILD.gn b/public/fidl/fuchsia.modular.storymodel/BUILD.gn
new file mode 100644
index 0000000..0cbb358
--- /dev/null
+++ b/public/fidl/fuchsia.modular.storymodel/BUILD.gn
@@ -0,0 +1,16 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/fidl/fidl.gni")
+
+fidl("fuchsia.modular.storymodel") {
+  sources = [
+    "constants.fidl",
+    "story_model.fidl",
+    "story_model_mutation.fidl",
+  ]
+  deps = [
+    "//peridot/public/fidl/fuchsia.modular",
+  ]
+}
diff --git a/public/fidl/fuchsia.modular.storymodel/constants.fidl b/public/fidl/fuchsia.modular.storymodel/constants.fidl
new file mode 100644
index 0000000..5630ec1
--- /dev/null
+++ b/public/fidl/fuchsia.modular.storymodel/constants.fidl
@@ -0,0 +1,10 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.modular.storymodel;
+
+const uint32 MAX_STORY_NAME_LENGTH = 1024;
+
+const uint32 MAX_MODULES_PER_STORY = 128;
+const uint32 MAX_MODULE_NAME_LENGTH = 1024;
diff --git a/public/fidl/fuchsia.modular.storymodel/story_model.fidl b/public/fidl/fuchsia.modular.storymodel/story_model.fidl
new file mode 100644
index 0000000..8bf437d
--- /dev/null
+++ b/public/fidl/fuchsia.modular.storymodel/story_model.fidl
@@ -0,0 +1,39 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.modular.storymodel;
+
+using fuchsia.modular;
+
+// IMPORTANT: StoryModel must only contain field types that are cloneable.
+//
+// The `StoryModel` FIDL table is used to represent the state of a story.
+// `sessionmgr` keeps a separate `StoryModel` in memory for each running story,
+// and also persists changes to it onto storage.
+table StoryModel {
+  // The name of the story, set at story create time.
+  //
+  // Always set. Immutable.
+  1: string:MAX_STORY_NAME_LENGTH name;
+
+  // An enum describing how the story should be displayed, when focused,
+  // in the StoryShell.
+  //
+  // Always set. Defaults to StoryVisibilityState::DEFAULT.
+  2: fuchsia.modular.StoryVisibilityState visibility_state;
+
+  // A list of modules present in the story.
+  //
+  // Always set. Defaults to an empty list.
+  3: vector<ModuleModel>:MAX_MODULES_PER_STORY modules;
+};
+
+table ModuleModel {
+  // The name of the module, set by the client that requested creation
+  // of the module. The name uniquely identifies this module within
+  // the story.
+  //
+  // Always set. Immutable.
+  1: string:MAX_MODULE_NAME_LENGTH name;
+};
\ No newline at end of file
diff --git a/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl b/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl
new file mode 100644
index 0000000..05d6f15
--- /dev/null
+++ b/public/fidl/fuchsia.modular.storymodel/story_model_mutation.fidl
@@ -0,0 +1,19 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fuchsia.modular.storymodel;
+
+using fuchsia.modular;
+
+// The `StoryModelMutation` union represents the set of all possible low-level mutations to data
+// for a single story. A vector of mutations represent mutations that are to be applied to the
+// model in a single transaction.
+//
+// This structured is used internally in `sessionmgr` and is not exposed to any clients outside
+// that process. Clients will typically construct these indirectly using convenience methods on the
+// `StoryMutator` class.
+union StoryModelMutation {
+  // Sets the value of |StoryModel.visibility_state|.
+  fuchsia.modular.StoryVisibilityState set_visibility_state;
+};
diff --git a/public/fidl/fuchsia.modular/story/story_state.fidl b/public/fidl/fuchsia.modular/story/story_state.fidl
index eb877f7..797435a 100644
--- a/public/fidl/fuchsia.modular/story/story_state.fidl
+++ b/public/fidl/fuchsia.modular/story/story_state.fidl
@@ -13,7 +13,6 @@
 //   STOPPED  -> RUNNING
 //   RUNNING  -> STOPPING
 //   STOPPING -> STOPPED
-//
 enum StoryState {
     // Story was started using StoryController.Start().
     RUNNING = 1;
diff --git a/public/lib/fostr/fidl/fuchsia.modular.storymodel/BUILD.gn b/public/lib/fostr/fidl/fuchsia.modular.storymodel/BUILD.gn
new file mode 100644
index 0000000..de4b509
--- /dev/null
+++ b/public/lib/fostr/fidl/fuchsia.modular.storymodel/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2018 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//garnet/public/build/fostr/fostr_fidl.gni")
+
+fostr_fidl("fuchsia.modular.storymodel") {
+  fidl_target = "//peridot/public/fidl/fuchsia.modular.storymodel"
+
+  deps = [
+    "//peridot/public/lib/fostr/fidl/fuchsia.modular",
+  ]
+}